<!DOCTYPE html>
<html lang=zh>
<head>
  <meta charset="utf-8">
  
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, minimal-ui">
  <meta name="renderer" content="webkit">
  <meta http-equiv="Cache-Control" content="no-transform" />
  <meta http-equiv="Cache-Control" content="no-siteapp" />
  <meta name="apple-mobile-web-app-capable" content="yes">
  <meta name="apple-mobile-web-app-status-bar-style" content="black">
  <meta name="format-detection" content="telephone=no,email=no,adress=no">
  <!-- Color theme for statusbar -->
  <meta name="theme-color" content="#000000" />
  <!-- 强制页面在当前窗口以独立页面显示,防止别人在框架里调用页面 -->
  <meta http-equiv="window-target" content="_top" />
  
  
  <title>JVM并发编程 | Skyung-Blog</title>
  <meta name="description" content="笔记参考黑马程序员java并发编程教程，https:&#x2F;&#x2F;www.bilibili.com&#x2F;video&#x2F;BV16J411h7Rd?p&#x3D;2&amp;share_source&#x3D;copy_web 概念进程与线程进程 程序由指令和数据组成，但这些指令要运行，数据要读写，就必须将指令加载至 CPU，数据加载至内存。在指令运行过程中还需要用到磁盘、网络等设备。进程就是用来加载指令、管理内存、管理 IO 的。 当">
<meta property="og:type" content="article">
<meta property="og:title" content="JVM并发编程">
<meta property="og:url" content="https://skyung.gitee.io/Skyung/2022/04/20/java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/index.html">
<meta property="og:site_name" content="Skyung-Blog">
<meta property="og:description" content="笔记参考黑马程序员java并发编程教程，https:&#x2F;&#x2F;www.bilibili.com&#x2F;video&#x2F;BV16J411h7Rd?p&#x3D;2&amp;share_source&#x3D;copy_web 概念进程与线程进程 程序由指令和数据组成，但这些指令要运行，数据要读写，就必须将指令加载至 CPU，数据加载至内存。在指令运行过程中还需要用到磁盘、网络等设备。进程就是用来加载指令、管理内存、管理 IO 的。 当">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220314161808081.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220415105816401.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220415110242622.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220415131825339.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220408144100948.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220408144703862.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220408153809828.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220414163820538.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220412144955927.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220412145014056.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220412145032711.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220412145103675.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220412145249874.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220412145300301.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220412145310768.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220412145520879.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220412145647660.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220412152319522.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220415170030594.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220415171107232.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220415171602176.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220416101640650.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220416101712436.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220416101748929.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220416101826307.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220416153327099.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220417125508384.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220417155150081.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220417182803347.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220417171841641.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220417171927023.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220417172315406.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220417181958557.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220417182113673.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220417182208894.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220417182414703.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220417182547570.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220418094529627.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220418094549352.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220418094645154.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220418094702592.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220418094837176.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220418094912468.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220418153312998.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220418154455649.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220418154521364.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220418154547256.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220418154652111.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220418215022192.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220418215508562.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220418215629529.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220418215915222.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220418220917582.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220418220955063.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220419103513420.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220419103537202.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220419103622104.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220420091833438.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220420102307686.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220420104958198.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220420105206867.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220420105421530.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220420105440880.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220420105521262.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220420105553094.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220420105658637.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220413144556916.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220413153953496.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220413160953761.png">
<meta property="og:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220407152321768.png">
<meta property="article:published_time" content="2022-04-19T16:00:00.000Z">
<meta property="article:modified_time" content="2022-04-19T16:00:00.000Z">
<meta property="article:author" content="Skyung">
<meta property="article:tag" content="java">
<meta property="article:tag" content="并发编程">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220314161808081.png">
  <!-- Canonical links -->
  <link rel="canonical" href="https://skyung.gitee.io/Skyung/2022/04/20/java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/index.html">
  
    <link rel="alternate" href="/atom.xml" title="Skyung-Blog" type="application/atom+xml">
  
  
    <link rel="icon" href="images/favicon.png" type="image/x-icon">
  
  
<link rel="stylesheet" href="/Skyung/css/style.css">

  
  
  
  
<meta name="generator" content="Hexo 5.4.1"></head>


<body class="main-center" itemscope itemtype="http://schema.org/WebPage">
  <header class="header" itemscope itemtype="http://schema.org/WPHeader">
  <div class="slimContent">
    <div class="navbar-header">
      
      
      <div class="profile-block text-center">
        <a id="avatar" href="https://github.com/cofess" target="_blank">
          <img class="img-circle img-rotate" src="/Skyung/images/avatar.jpg" width="200" height="200">
        </a>
        <h2 id="name" class="hidden-xs hidden-sm">Skyung</h2>
        <h3 id="title" class="hidden-xs hidden-sm hidden-md">Web Developer</h3>
        <small id="location" class="text-muted hidden-xs hidden-sm"><i class="icon icon-map-marker"></i> Wuhan, China</small>
      </div>
      
      <div class="search" id="search-form-wrap">

    <form class="search-form sidebar-form">
        <div class="input-group">
            <input type="text" class="search-form-input form-control" placeholder="搜索" />
            <span class="input-group-btn">
                <button type="submit" class="search-form-submit btn btn-flat" onclick="return false;"><i class="icon icon-search"></i></button>
            </span>
        </div>
    </form>
    <div class="ins-search">
  <div class="ins-search-mask"></div>
  <div class="ins-search-container">
    <div class="ins-input-wrapper">
      <input type="text" class="ins-search-input" placeholder="想要查找什么..." x-webkit-speech />
      <button type="button" class="close ins-close ins-selectable" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
    </div>
    <div class="ins-section-wrapper">
      <div class="ins-section-container"></div>
    </div>
  </div>
</div>


</div>
      <button class="navbar-toggle collapsed" type="button" data-toggle="collapse" data-target="#main-navbar" aria-controls="main-navbar" aria-expanded="false">
        <span class="sr-only">Toggle navigation</span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
        <span class="icon-bar"></span>
      </button>
    </div>
    <nav id="main-navbar" class="collapse navbar-collapse" itemscope itemtype="http://schema.org/SiteNavigationElement" role="navigation">
      <ul class="nav navbar-nav main-nav ">
        
        
        <li class="menu-item menu-item-home">
          <a href="/Skyung/.">
            
            <i class="icon icon-home-fill"></i>
            
            <span class="menu-title">首页</span>
          </a>
        </li>
        
        
        <li class="menu-item menu-item-archives">
          <a href="/Skyung/archives">
            
            <i class="icon icon-archives-fill"></i>
            
            <span class="menu-title">归档</span>
          </a>
        </li>
        
        
        <li class="menu-item menu-item-categories">
          <a href="/Skyung/categories">
            
            <i class="icon icon-folder"></i>
            
            <span class="menu-title">分类</span>
          </a>
        </li>
        
        
        <li class="menu-item menu-item-tags">
          <a href="/Skyung/tags">
            
            <i class="icon icon-tags"></i>
            
            <span class="menu-title">标签</span>
          </a>
        </li>
        
        
        <li class="menu-item menu-item-repository">
          <a href="/Skyung/repository">
            
            <i class="icon icon-project"></i>
            
            <span class="menu-title">项目</span>
          </a>
        </li>
        
        
        <li class="menu-item menu-item-books">
          <a href="/Skyung/books">
            
            <i class="icon icon-book-fill"></i>
            
            <span class="menu-title">书单</span>
          </a>
        </li>
        
        
        <li class="menu-item menu-item-links">
          <a href="/Skyung/links">
            
            <i class="icon icon-friendship"></i>
            
            <span class="menu-title">友链</span>
          </a>
        </li>
        
        
        <li class="menu-item menu-item-about">
          <a href="/Skyung/about">
            
            <i class="icon icon-cup-fill"></i>
            
            <span class="menu-title">关于</span>
          </a>
        </li>
        
      </ul>
      
	
    <ul class="social-links">
    	
        <li><a href="/Skyung/null" target="_blank" title="Github" data-toggle=tooltip data-placement=top><i class="icon icon-github"></i></a></li>
        
        <li><a href="/Skyung/null" target="_blank" title="Weibo" data-toggle=tooltip data-placement=top><i class="icon icon-weibo"></i></a></li>
        
        <li><a href="/Skyung/null" target="_blank" title="Twitter" data-toggle=tooltip data-placement=top><i class="icon icon-twitter"></i></a></li>
        
        <li><a href="/Skyung/null" target="_blank" title="Behance" data-toggle=tooltip data-placement=top><i class="icon icon-behance"></i></a></li>
        
        <li><a href="/Skyung/atom.xml" target="_blank" title="Rss" data-toggle=tooltip data-placement=top><i class="icon icon-rss"></i></a></li>
        
    </ul>

    </nav>
  </div>
</header>

  
    <aside class="sidebar" itemscope itemtype="http://schema.org/WPSideBar">
  <div class="slimContent">
    
      <div class="widget">
    <h3 class="widget-title">公告</h3>
    <div class="widget-body">
        <div id="board">
            <div class="content">
                <p>欢迎交流与分享经验!</p>
            </div>
        </div>
    </div>
</div>

    
      
  <div class="widget">
    <h3 class="widget-title">分类</h3>
    <div class="widget-body">
      <ul class="category-list"><li class="category-list-item"><a class="category-list-link" href="/Skyung/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</a><span class="category-list-count">20</span></li></ul>
    </div>
  </div>


    
      
  <div class="widget">
    <h3 class="widget-title">标签</h3>
    <div class="widget-body">
      <ul class="tag-list" itemprop="keywords"><li class="tag-list-item"><a class="tag-list-link" href="/Skyung/tags/java/" rel="tag">java</a><span class="tag-list-count">19</span></li><li class="tag-list-item"><a class="tag-list-link" href="/Skyung/tags/jvm/" rel="tag">jvm</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/Skyung/tags/linux/" rel="tag">linux</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/Skyung/tags/redis/" rel="tag">redis</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/Skyung/tags/%E5%A4%9A%E7%BA%BF%E7%A8%8B/" rel="tag">多线程</a><span class="tag-list-count">1</span></li><li class="tag-list-item"><a class="tag-list-link" href="/Skyung/tags/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/" rel="tag">并发编程</a><span class="tag-list-count">2</span></li><li class="tag-list-item"><a class="tag-list-link" href="/Skyung/tags/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/" rel="tag">设计模式</a><span class="tag-list-count">4</span></li><li class="tag-list-item"><a class="tag-list-link" href="/Skyung/tags/%E9%9B%B6%E6%95%A3%E7%82%B9/" rel="tag">零散点</a><span class="tag-list-count">1</span></li></ul>
    </div>
  </div>


    
      
  <div class="widget">
    <h3 class="widget-title">标签云</h3>
    <div class="widget-body tagcloud">
      <a href="/Skyung/tags/java/" style="font-size: 14px;">java</a> <a href="/Skyung/tags/jvm/" style="font-size: 13px;">jvm</a> <a href="/Skyung/tags/linux/" style="font-size: 13px;">linux</a> <a href="/Skyung/tags/redis/" style="font-size: 13px;">redis</a> <a href="/Skyung/tags/%E5%A4%9A%E7%BA%BF%E7%A8%8B/" style="font-size: 13px;">多线程</a> <a href="/Skyung/tags/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/" style="font-size: 13.33px;">并发编程</a> <a href="/Skyung/tags/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/" style="font-size: 13.67px;">设计模式</a> <a href="/Skyung/tags/%E9%9B%B6%E6%95%A3%E7%82%B9/" style="font-size: 13px;">零散点</a>
    </div>
  </div>

    
      
  <div class="widget">
    <h3 class="widget-title">归档</h3>
    <div class="widget-body">
      <ul class="archive-list"><li class="archive-list-item"><a class="archive-list-link" href="/Skyung/archives/2022/05/">五月 2022</a><span class="archive-list-count">5</span></li><li class="archive-list-item"><a class="archive-list-link" href="/Skyung/archives/2022/04/">四月 2022</a><span class="archive-list-count">7</span></li><li class="archive-list-item"><a class="archive-list-link" href="/Skyung/archives/2022/03/">三月 2022</a><span class="archive-list-count">9</span></li><li class="archive-list-item"><a class="archive-list-link" href="/Skyung/archives/2022/02/">二月 2022</a><span class="archive-list-count">1</span></li><li class="archive-list-item"><a class="archive-list-link" href="/Skyung/archives/2021/09/">九月 2021</a><span class="archive-list-count">1</span></li></ul>
    </div>
  </div>


    
      
  <div class="widget">
    <h3 class="widget-title">最新文章</h3>
    <div class="widget-body">
      <ul class="recent-post-list list-unstyled no-thumbnail">
        
          <li>
            
            <div class="item-inner">
              <p class="item-category">
                <a class="category-link" href="/Skyung/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</a>
              </p>
              <p class="item-title">
                <a href="/Skyung/2022/05/08/Spring/" class="title">spring</a>
              </p>
              <p class="item-date">
                <time datetime="2022-05-07T16:00:00.000Z" itemprop="datePublished">2022-05-08</time>
              </p>
            </div>
          </li>
          
          <li>
            
            <div class="item-inner">
              <p class="item-category">
                <a class="category-link" href="/Skyung/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</a>
              </p>
              <p class="item-title">
                <a href="/Skyung/2022/05/08/netty-RPC/" class="title">Netty-RPC</a>
              </p>
              <p class="item-date">
                <time datetime="2022-05-07T16:00:00.000Z" itemprop="datePublished">2022-05-08</time>
              </p>
            </div>
          </li>
          
          <li>
            
            <div class="item-inner">
              <p class="item-category">
                <a class="category-link" href="/Skyung/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</a>
              </p>
              <p class="item-title">
                <a href="/Skyung/2022/05/07/%E5%B8%B8%E7%94%A8%E7%B1%BB%E5%BA%93%E5%92%8CAPI/" class="title">常见类库</a>
              </p>
              <p class="item-date">
                <time datetime="2022-05-06T16:00:00.000Z" itemprop="datePublished">2022-05-07</time>
              </p>
            </div>
          </li>
          
          <li>
            
            <div class="item-inner">
              <p class="item-category">
                <a class="category-link" href="/Skyung/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</a>
              </p>
              <p class="item-title">
                <a href="/Skyung/2022/05/07/%E5%8D%95%E5%85%83%E6%B5%8B%E8%AF%95/" class="title">单元测试</a>
              </p>
              <p class="item-date">
                <time datetime="2022-05-06T16:00:00.000Z" itemprop="datePublished">2022-05-07</time>
              </p>
            </div>
          </li>
          
          <li>
            
            <div class="item-inner">
              <p class="item-category">
                <a class="category-link" href="/Skyung/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</a>
              </p>
              <p class="item-title">
                <a href="/Skyung/2022/05/01/netty%E8%81%8A%E5%A4%A9%E5%AE%A4/" class="title">Netty聊天室</a>
              </p>
              <p class="item-date">
                <time datetime="2022-04-30T16:00:00.000Z" itemprop="datePublished">2022-05-01</time>
              </p>
            </div>
          </li>
          
      </ul>
    </div>
  </div>
  

    
  </div>
</aside>

  
  
  <aside class="sidebar sidebar-toc collapse   in  " id="collapseToc" itemscope itemtype="http://schema.org/WPSideBar">
  <div class="slimContent">
    <nav id="toc" class="article-toc">
      <h3 class="toc-title">文章目录</h3>
      <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#%E6%A6%82%E5%BF%B5"><span class="toc-number">1.</span> <span class="toc-text">概念</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E8%BF%9B%E7%A8%8B%E4%B8%8E%E7%BA%BF%E7%A8%8B"><span class="toc-number">1.1.</span> <span class="toc-text">进程与线程</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%BF%9B%E7%A8%8B"><span class="toc-number">1.1.1.</span> <span class="toc-text">进程</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%BA%BF%E7%A8%8B"><span class="toc-number">1.1.2.</span> <span class="toc-text">线程</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%BA%8C%E8%80%85%E5%AF%B9%E6%AF%94"><span class="toc-number">1.1.3.</span> <span class="toc-text">二者对比</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%B9%B6%E8%A1%8C%E5%92%8C%E5%B9%B6%E5%8F%91"><span class="toc-number">1.1.4.</span> <span class="toc-text">并行和并发</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%BA%94%E7%94%A8"><span class="toc-number">1.1.5.</span> <span class="toc-text">应用</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#JMM"><span class="toc-number">2.</span> <span class="toc-text">JMM</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8E%9F%E5%AD%90%E6%80%A7"><span class="toc-number">2.1.</span> <span class="toc-text">原子性</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%97%AE%E9%A2%98%E5%88%86%E6%9E%90"><span class="toc-number">2.1.1.</span> <span class="toc-text">问题分析</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95"><span class="toc-number">2.1.2.</span> <span class="toc-text">解决方法</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8F%AF%E8%A7%81%E6%80%A7"><span class="toc-number">2.2.</span> <span class="toc-text">可见性</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%97%AE%E9%A2%98%E5%88%86%E6%9E%90-1"><span class="toc-number">2.2.1.</span> <span class="toc-text">问题分析</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95-1"><span class="toc-number">2.2.2.</span> <span class="toc-text">解决方法</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%9C%89%E5%BA%8F%E6%80%A7"><span class="toc-number">2.3.</span> <span class="toc-text">有序性</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%8E%9F%E7%90%86%E4%B9%8B%E6%8C%87%E4%BB%A4%E9%87%8D%E6%8E%92"><span class="toc-number">2.3.1.</span> <span class="toc-text">原理之指令重排</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%97%AE%E9%A2%98%E5%88%86%E6%9E%90-2"><span class="toc-number">2.3.2.</span> <span class="toc-text">问题分析</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#double-checked-locking%E9%97%AE%E9%A2%98"><span class="toc-number">2.3.2.1.</span> <span class="toc-text">double-checked locking问题</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%8C%87%E4%BB%A4%E9%87%8D%E6%8E%92%E9%97%AE%E9%A2%98"><span class="toc-number">2.3.2.2.</span> <span class="toc-text">指令重排问题</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%A7%A3%E5%86%B3%E6%96%B9%E6%B3%95-2"><span class="toc-number">2.3.3.</span> <span class="toc-text">解决方法</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%8E%9F%E7%90%86%E4%B9%8Bvolatile"><span class="toc-number">2.3.4.</span> <span class="toc-text">原理之volatile</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#happens-before"><span class="toc-number">2.3.5.</span> <span class="toc-text">happens-before</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#java%E7%BA%BF%E7%A8%8B"><span class="toc-number">3.</span> <span class="toc-text">java线程</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%88%9B%E5%BB%BA%E5%92%8C%E8%BF%90%E8%A1%8C%E7%BA%BF%E7%A8%8B"><span class="toc-number">3.1.</span> <span class="toc-text">创建和运行线程</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%9F%A5%E7%9C%8B%E8%BF%9B%E7%A8%8B%E5%92%8C%E7%BA%BF%E7%A8%8B"><span class="toc-number">3.2.</span> <span class="toc-text">查看进程和线程</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%BA%BF%E7%A8%8B%E8%BF%90%E8%A1%8C%E5%8E%9F%E7%90%86"><span class="toc-number">3.3.</span> <span class="toc-text">线程运行原理</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%B8%B8%E8%A7%81%E6%96%B9%E6%B3%95"><span class="toc-number">3.4.</span> <span class="toc-text">常见方法</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#start-%E4%B8%8E-run"><span class="toc-number">3.4.1.</span> <span class="toc-text">start 与 run</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#sleep-%E4%B8%8E-yield"><span class="toc-number">3.4.2.</span> <span class="toc-text">sleep 与 yield</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#join%E6%96%B9%E6%B3%95"><span class="toc-number">3.4.3.</span> <span class="toc-text">join方法</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#interrupt%E6%96%B9%E6%B3%95"><span class="toc-number">3.4.4.</span> <span class="toc-text">interrupt方法</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%B8%8D%E6%8E%A8%E8%8D%90%E7%9A%84%E6%96%B9%E6%B3%95"><span class="toc-number">3.4.5.</span> <span class="toc-text">不推荐的方法</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%B8%BB%E7%BA%BF%E7%A8%8B%E5%92%8C%E5%AE%88%E6%8A%A4%E7%BA%BF%E7%A8%8B"><span class="toc-number">3.5.</span> <span class="toc-text">主线程和守护线程</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%BA%BF%E7%A8%8B%E7%9A%84%E7%8A%B6%E6%80%81"><span class="toc-number">3.6.</span> <span class="toc-text">线程的状态</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%BA%BF%E7%A8%8B%E7%8A%B6%E6%80%81%E7%9A%84%E8%BD%AC%E6%8D%A2"><span class="toc-number">3.6.1.</span> <span class="toc-text">线程状态的转换</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%90%88%E7%90%86%E8%BF%90%E7%94%A8%E7%BA%BF%E7%A8%8B"><span class="toc-number">3.7.</span> <span class="toc-text">合理运用线程</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%85%B1%E4%BA%AB%E6%A8%A1%E5%9E%8B%E4%B9%8B%E7%AE%A1%E7%A8%8B"><span class="toc-number">4.</span> <span class="toc-text">共享模型之管程</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8F%98%E9%87%8F%E7%9A%84%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E5%88%86%E6%9E%90"><span class="toc-number">4.1.</span> <span class="toc-text">变量的线程安全分析</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%B8%B8%E8%A7%81%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E7%B1%BB"><span class="toc-number">4.2.</span> <span class="toc-text">常见线程安全类</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8E%9F%E7%90%86%E4%B9%8Bsynchronized"><span class="toc-number">4.3.</span> <span class="toc-text">原理之synchronized</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#Monitor"><span class="toc-number">4.3.1.</span> <span class="toc-text">Monitor</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#java%E5%AF%B9%E8%B1%A1%E5%A4%B4"><span class="toc-number">4.3.2.</span> <span class="toc-text">java对象头</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#synchronized%E4%BC%98%E5%8C%96"><span class="toc-number">4.4.</span> <span class="toc-text">synchronized优化</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%BD%BB%E9%87%8F%E7%BA%A7%E9%94%81"><span class="toc-number">4.4.1.</span> <span class="toc-text">轻量级锁</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%94%81%E8%86%A8%E8%83%80"><span class="toc-number">4.4.2.</span> <span class="toc-text">锁膨胀</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%87%8D%E9%87%8F%E9%94%81%E8%87%AA%E6%97%8B%E4%BC%98%E5%8C%96"><span class="toc-number">4.4.3.</span> <span class="toc-text">重量锁自旋优化</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%81%8F%E5%90%91%E9%94%81"><span class="toc-number">4.4.4.</span> <span class="toc-text">偏向锁</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%85%B6%E4%BB%96%E4%BC%98%E5%8C%96"><span class="toc-number">4.4.5.</span> <span class="toc-text">其他优化</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%87%8F%E5%B0%91%E4%B8%8A%E9%94%81%E6%97%B6%E9%97%B4"><span class="toc-number">4.4.5.1.</span> <span class="toc-text">减少上锁时间</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%87%8F%E5%B0%91%E9%94%81%E7%9A%84%E7%B2%92%E5%BA%A6"><span class="toc-number">4.4.5.2.</span> <span class="toc-text">减少锁的粒度</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E9%94%81%E7%B2%97%E5%8C%96"><span class="toc-number">4.4.5.3.</span> <span class="toc-text">锁粗化</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E9%94%81%E6%B6%88%E9%99%A4"><span class="toc-number">4.4.5.4.</span> <span class="toc-text">锁消除</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E8%AF%BB%E5%86%99%E5%88%86%E7%A6%BB"><span class="toc-number">4.4.5.5.</span> <span class="toc-text">读写分离</span></a></li></ol></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#wait-notify"><span class="toc-number">4.5.</span> <span class="toc-text">wait notify</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%8E%9F%E7%90%86%E4%B9%8Bwait-notify"><span class="toc-number">4.5.1.</span> <span class="toc-text">原理之wait notify</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#API"><span class="toc-number">4.5.2.</span> <span class="toc-text">API</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#sleep%E5%92%8Cwait"><span class="toc-number">4.5.3.</span> <span class="toc-text">sleep和wait</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%94%A8%E6%B3%95"><span class="toc-number">4.5.4.</span> <span class="toc-text">用法</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#park%E5%92%8Cunpark"><span class="toc-number">4.6.</span> <span class="toc-text">park和unpark</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%8E%9F%E7%90%86%E4%B9%8Bpark%E5%92%8Cunpark"><span class="toc-number">4.6.0.1.</span> <span class="toc-text">原理之park和unpark</span></a></li></ol></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%A4%9A%E6%8A%8A%E9%94%81"><span class="toc-number">4.7.</span> <span class="toc-text">多把锁</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%B4%BB%E8%B7%83%E6%80%A7"><span class="toc-number">4.8.</span> <span class="toc-text">活跃性</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%AD%BB%E9%94%81"><span class="toc-number">4.8.1.</span> <span class="toc-text">死锁</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%B4%BB%E9%94%81"><span class="toc-number">4.8.2.</span> <span class="toc-text">活锁</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%A5%A5%E9%A5%BF"><span class="toc-number">4.8.3.</span> <span class="toc-text">饥饿</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#ReentrantLock"><span class="toc-number">4.9.</span> <span class="toc-text">ReentrantLock</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%8F%AF%E9%87%8D%E5%85%A5"><span class="toc-number">4.9.1.</span> <span class="toc-text">可重入</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%8F%AF%E6%89%93%E6%96%AD"><span class="toc-number">4.9.2.</span> <span class="toc-text">可打断</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%94%81%E8%B6%85%E6%97%B6"><span class="toc-number">4.9.3.</span> <span class="toc-text">锁超时</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%85%AC%E5%B9%B3%E9%94%81"><span class="toc-number">4.9.4.</span> <span class="toc-text">公平锁</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%9D%A1%E4%BB%B6%E5%8F%98%E9%87%8F"><span class="toc-number">4.9.5.</span> <span class="toc-text">条件变量</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%85%B1%E4%BA%AB%E6%A8%A1%E5%9E%8B%E4%B9%8B%E6%97%A0%E9%94%81"><span class="toc-number">5.</span> <span class="toc-text">共享模型之无锁</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#CAS-%E4%B8%8E-volatile"><span class="toc-number">5.1.</span> <span class="toc-text">CAS 与 volatile</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%BC%98%E5%8A%BF"><span class="toc-number">5.1.1.</span> <span class="toc-text">优势</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%B9%90%E8%A7%82%E9%94%81%E4%B8%8E%E6%82%B2%E8%A7%82%E9%94%81"><span class="toc-number">5.1.2.</span> <span class="toc-text">乐观锁与悲观锁</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%8E%9F%E5%AD%90%E6%93%8D%E4%BD%9C%E7%B1%BB"><span class="toc-number">5.2.</span> <span class="toc-text">原子操作类</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%8E%9F%E5%AD%90%E6%95%B4%E6%95%B0"><span class="toc-number">5.2.1.</span> <span class="toc-text">原子整数</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%8E%9F%E5%AD%90%E5%BC%95%E7%94%A8"><span class="toc-number">5.2.2.</span> <span class="toc-text">原子引用</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#ABA-%E9%97%AE%E9%A2%98%E5%8F%8A%E8%A7%A3%E5%86%B3"><span class="toc-number">5.2.2.1.</span> <span class="toc-text">ABA 问题及解决</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%8E%9F%E5%AD%90%E6%95%B0%E7%BB%84"><span class="toc-number">5.2.3.</span> <span class="toc-text">原子数组</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%AD%97%E6%AE%B5%E6%9B%B4%E6%96%B0%E5%99%A8"><span class="toc-number">5.2.4.</span> <span class="toc-text">字段更新器</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%8E%9F%E5%AD%90%E7%B4%AF%E5%8A%A0%E5%99%A8"><span class="toc-number">5.2.5.</span> <span class="toc-text">原子累加器</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#cas-%E9%94%81"><span class="toc-number">5.2.5.1.</span> <span class="toc-text">cas 锁</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%8E%9F%E7%90%86%E4%B9%8B%E4%BC%AA%E5%85%B1%E4%BA%AB"><span class="toc-number">5.2.5.2.</span> <span class="toc-text">原理之伪共享</span></a></li></ol></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#Unsafe"><span class="toc-number">5.3.</span> <span class="toc-text">Unsafe</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%85%B1%E4%BA%AB%E6%A8%A1%E5%9E%8B%E4%B9%8B%E4%B8%8D%E5%8F%AF%E5%8F%98"><span class="toc-number">6.</span> <span class="toc-text">共享模型之不可变</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%97%A5%E6%9C%9F%E8%BD%AC%E6%8D%A2%E7%9A%84%E9%97%AE%E9%A2%98"><span class="toc-number">6.1.</span> <span class="toc-text">日期转换的问题</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%B8%8D%E5%8F%AF%E5%8F%98%E8%AE%BE%E8%AE%A1"><span class="toc-number">6.2.</span> <span class="toc-text">不可变设计</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#final-%E7%9A%84%E4%BD%BF%E7%94%A8"><span class="toc-number">6.2.1.</span> <span class="toc-text">final 的使用</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#final%E5%8E%9F%E7%90%86"><span class="toc-number">6.2.1.1.</span> <span class="toc-text">final原理</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%BF%9D%E6%8A%A4%E6%80%A7%E6%8B%B7%E8%B4%9D"><span class="toc-number">6.2.2.</span> <span class="toc-text">保护性拷贝</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%97%A0%E7%8A%B6%E6%80%81"><span class="toc-number">6.3.</span> <span class="toc-text">无状态</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%85%B1%E4%BA%AB%E6%A8%A1%E5%9E%8B%E4%B9%8B%E5%B9%B6%E5%8F%91%E5%B7%A5%E5%85%B7"><span class="toc-number">7.</span> <span class="toc-text">共享模型之并发工具</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E7%BA%BF%E7%A8%8B%E6%B1%A0"><span class="toc-number">7.1.</span> <span class="toc-text">线程池</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%87%AA%E5%AE%9A%E4%B9%89%E7%BA%BF%E7%A8%8B%E6%B1%A0"><span class="toc-number">7.1.1.</span> <span class="toc-text">自定义线程池</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#ThreadPoolExecutor"><span class="toc-number">7.1.2.</span> <span class="toc-text">ThreadPoolExecutor</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E7%BA%BF%E7%A8%8B%E6%B1%A0%E7%8A%B6%E6%80%81"><span class="toc-number">7.1.2.1.</span> <span class="toc-text">线程池状态</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%9E%84%E9%80%A0%E6%96%B9%E6%B3%95"><span class="toc-number">7.1.2.2.</span> <span class="toc-text">构造方法</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#newFixedThreadPool"><span class="toc-number">7.1.2.3.</span> <span class="toc-text">newFixedThreadPool</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#newCachedThreadPool"><span class="toc-number">7.1.2.4.</span> <span class="toc-text">newCachedThreadPool</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#newSingleThreadExecutor"><span class="toc-number">7.1.2.5.</span> <span class="toc-text">newSingleThreadExecutor</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%8F%90%E4%BA%A4%E4%BB%BB%E5%8A%A1"><span class="toc-number">7.1.2.6.</span> <span class="toc-text">提交任务</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%85%B3%E9%97%AD%E7%BA%BF%E7%A8%8B%E6%B1%A0"><span class="toc-number">7.1.2.7.</span> <span class="toc-text">关闭线程池</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E4%BB%BB%E5%8A%A1%E8%B0%83%E5%BA%A6%E7%BA%BF%E7%A8%8B%E6%B1%A0"><span class="toc-number">7.1.2.8.</span> <span class="toc-text">任务调度线程池</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%AD%A3%E7%A1%AE%E5%A4%84%E7%90%86%E6%89%A7%E8%A1%8C%E4%BB%BB%E5%8A%A1%E5%BC%82%E5%B8%B8"><span class="toc-number">7.1.2.9.</span> <span class="toc-text">正确处理执行任务异常</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#Tomcat-%E7%BA%BF%E7%A8%8B%E6%B1%A0"><span class="toc-number">7.1.2.10.</span> <span class="toc-text">Tomcat 线程池</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%BA%94%E7%94%A8-1"><span class="toc-number">7.1.2.11.</span> <span class="toc-text">应用</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#Fork-Join"><span class="toc-number">7.1.3.</span> <span class="toc-text">Fork&#x2F;Join</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%A6%82%E5%BF%B5-1"><span class="toc-number">7.1.3.1.</span> <span class="toc-text">概念</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E4%BD%BF%E7%94%A8"><span class="toc-number">7.1.3.2.</span> <span class="toc-text">使用</span></a></li></ol></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#JUC"><span class="toc-number">7.2.</span> <span class="toc-text">JUC</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#AQS"><span class="toc-number">7.2.1.</span> <span class="toc-text">AQS</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%A6%82%E8%BF%B0"><span class="toc-number">7.2.1.1.</span> <span class="toc-text">概述</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%AE%9E%E7%8E%B0%E4%B8%8D%E5%8F%AF%E9%87%8D%E5%85%A5%E9%94%81"><span class="toc-number">7.2.1.2.</span> <span class="toc-text">实现不可重入锁</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E8%AE%BE%E8%AE%A1"><span class="toc-number">7.2.1.3.</span> <span class="toc-text">设计</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E4%B8%BB%E8%A6%81%E7%94%A8%E5%88%B0-AQS-%E7%9A%84%E5%B9%B6%E5%8F%91%E5%B7%A5%E5%85%B7%E7%B1%BB"><span class="toc-number">7.2.1.4.</span> <span class="toc-text">主要用到 AQS 的并发工具类</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#ReentrantLock-1"><span class="toc-number">7.2.2.</span> <span class="toc-text">ReentrantLock</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E9%9D%9E%E5%85%AC%E5%B9%B3%E9%94%81%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86"><span class="toc-number">7.2.2.1.</span> <span class="toc-text">非公平锁实现原理</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%8F%AF%E9%87%8D%E5%85%A5%E5%8E%9F%E7%90%86"><span class="toc-number">7.2.2.2.</span> <span class="toc-text">可重入原理</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%8F%AF%E6%89%93%E6%96%AD%E5%8E%9F%E7%90%86"><span class="toc-number">7.2.2.3.</span> <span class="toc-text">可打断原理</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%85%AC%E5%B9%B3%E9%94%81%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86"><span class="toc-number">7.2.2.4.</span> <span class="toc-text">公平锁实现原理</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%9D%A1%E4%BB%B6%E5%8F%98%E9%87%8F%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86"><span class="toc-number">7.2.2.5.</span> <span class="toc-text">条件变量实现原理</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E8%AF%BB%E5%86%99%E9%94%81"><span class="toc-number">7.2.3.</span> <span class="toc-text">读写锁</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#ReentrantReadWriteLock"><span class="toc-number">7.2.3.1.</span> <span class="toc-text">ReentrantReadWriteLock</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%BA%94%E7%94%A8%E4%B9%8B%E7%BC%93%E5%AD%98"><span class="toc-number">7.2.3.2.</span> <span class="toc-text">应用之缓存</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E8%AF%BB%E5%86%99%E9%94%81%E5%8E%9F%E7%90%86"><span class="toc-number">7.2.3.3.</span> <span class="toc-text">读写锁原理</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E6%BA%90%E7%A0%81"><span class="toc-number">7.2.3.4.</span> <span class="toc-text">源码</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#StampedLock"><span class="toc-number">7.2.3.5.</span> <span class="toc-text">StampedLock</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#Semaphore"><span class="toc-number">7.2.4.</span> <span class="toc-text">Semaphore</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%BA%94%E7%94%A8%E2%80%94%E9%99%90%E5%88%B6%E5%AF%B9%E5%85%B1%E4%BA%AB%E8%B5%84%E6%BA%90%E7%9A%84%E4%BD%BF%E7%94%A8"><span class="toc-number">7.2.4.1.</span> <span class="toc-text">应用—限制对共享资源的使用</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%8E%9F%E7%90%86"><span class="toc-number">7.2.4.2.</span> <span class="toc-text">原理</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#CountdownLatch"><span class="toc-number">7.2.5.</span> <span class="toc-text">CountdownLatch</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#%E7%94%A8%E6%B3%95-1"><span class="toc-number">7.2.5.1.</span> <span class="toc-text">用法</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#%E5%BA%94%E7%94%A8-2"><span class="toc-number">7.2.5.2.</span> <span class="toc-text">应用</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#CyclicBarrier"><span class="toc-number">7.2.6.</span> <span class="toc-text">CyclicBarrier</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E9%9B%86%E5%90%88%E7%B1%BB"><span class="toc-number">7.2.7.</span> <span class="toc-text">线程安全集合类</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#ConcurrentHashMap"><span class="toc-number">7.2.8.</span> <span class="toc-text">ConcurrentHashMap</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#JDK-7-HashMap-%E5%B9%B6%E5%8F%91%E6%AD%BB%E9%93%BE%E9%97%AE%E9%A2%98"><span class="toc-number">7.2.8.1.</span> <span class="toc-text">JDK 7 HashMap 并发死链问题</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#JDK-8-ConcurrentHashMap"><span class="toc-number">7.2.8.2.</span> <span class="toc-text">JDK 8 ConcurrentHashMap</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#JDK-7-ConcurrentHashMap"><span class="toc-number">7.2.8.3.</span> <span class="toc-text">JDK 7 ConcurrentHashMap</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#BlockingQueue"><span class="toc-number">7.2.9.</span> <span class="toc-text">BlockingQueue</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#ConcurrentLinkedQueue"><span class="toc-number">7.2.10.</span> <span class="toc-text">ConcurrentLinkedQueue</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#CopyOnWriteArrayList"><span class="toc-number">7.2.11.</span> <span class="toc-text">CopyOnWriteArrayList</span></a></li></ol></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E7%9A%84%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F"><span class="toc-number">8.</span> <span class="toc-text">并发编程的设计模式</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%90%8C%E6%AD%A5%E6%A8%A1%E5%BC%8F%E4%B9%8B%E4%BF%9D%E6%8A%A4%E6%80%A7%E6%9A%82%E5%81%9C"><span class="toc-number">8.1.</span> <span class="toc-text">同步模式之保护性暂停</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%AE%9A%E4%B9%89"><span class="toc-number">8.1.1.</span> <span class="toc-text">定义</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%AE%9E%E7%8E%B0"><span class="toc-number">8.1.2.</span> <span class="toc-text">实现</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E6%89%A9%E5%B1%95"><span class="toc-number">8.1.3.</span> <span class="toc-text">扩展</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#join%E5%8E%9F%E7%90%86"><span class="toc-number">8.1.4.</span> <span class="toc-text">join原理</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%BC%82%E6%AD%A5%E6%A8%A1%E5%BC%8F%E4%B9%8B%E7%94%9F%E4%BA%A7%E8%80%85%E6%B6%88%E8%B4%B9%E8%80%85"><span class="toc-number">8.2.</span> <span class="toc-text">异步模式之生产者消费者</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%AE%9A%E4%B9%89-1"><span class="toc-number">8.2.1.</span> <span class="toc-text">定义</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%AE%9E%E7%8E%B0-1"><span class="toc-number">8.2.2.</span> <span class="toc-text">实现</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%90%8C%E6%AD%A5%E6%A8%A1%E5%BC%8F%E4%B9%8B%E9%A1%BA%E5%BA%8F%E6%8E%A7%E5%88%B6"><span class="toc-number">8.3.</span> <span class="toc-text">同步模式之顺序控制</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%9B%BA%E5%AE%9A%E8%BF%90%E8%A1%8C%E9%A1%BA%E5%BA%8F"><span class="toc-number">8.3.1.</span> <span class="toc-text">固定运行顺序</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#wait-notify-%E7%89%88"><span class="toc-number">8.3.1.1.</span> <span class="toc-text">wait-notify 版</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#Park-Unpark-%E7%89%88"><span class="toc-number">8.3.1.2.</span> <span class="toc-text">Park Unpark 版</span></a></li></ol></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E4%BA%A4%E6%9B%BF%E8%BE%93%E5%87%BA"><span class="toc-number">8.3.2.</span> <span class="toc-text">交替输出</span></a><ol class="toc-child"><li class="toc-item toc-level-5"><a class="toc-link" href="#wait-notify-%E7%89%88-1"><span class="toc-number">8.3.2.1.</span> <span class="toc-text">wait notify 版</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#Lock-%E6%9D%A1%E4%BB%B6%E5%8F%98%E9%87%8F%E7%89%88"><span class="toc-number">8.3.2.2.</span> <span class="toc-text">Lock 条件变量版</span></a></li><li class="toc-item toc-level-5"><a class="toc-link" href="#Park-Unpark-%E7%89%88-1"><span class="toc-number">8.3.2.3.</span> <span class="toc-text">Park Unpark 版</span></a></li></ol></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E4%B8%A4%E9%98%B6%E6%AE%B5%E7%BB%88%E6%AD%A2%E6%A8%A1%E5%BC%8F"><span class="toc-number">8.4.</span> <span class="toc-text">两阶段终止模式</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%90%8C%E6%AD%A5%E6%A8%A1%E5%BC%8F%E4%B9%8B-Balking"><span class="toc-number">8.5.</span> <span class="toc-text">同步模式之 Balking</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E6%A8%A1%E5%BC%8F%E4%B9%8B%E4%BA%AB%E5%85%83"><span class="toc-number">8.6.</span> <span class="toc-text">模式之享元</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%8C%85%E8%A3%85%E7%B1%BB"><span class="toc-number">8.6.1.</span> <span class="toc-text">包装类</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#String-%E4%B8%B2%E6%B1%A0"><span class="toc-number">8.6.2.</span> <span class="toc-text">String 串池</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%BA%94%E7%94%A8-3"><span class="toc-number">8.6.3.</span> <span class="toc-text">应用</span></a></li></ol></li><li class="toc-item toc-level-3"><a class="toc-link" href="#%E5%BC%82%E6%AD%A5%E6%A8%A1%E5%BC%8F%E4%B9%8B%E5%B7%A5%E4%BD%9C%E7%BA%BF%E7%A8%8B"><span class="toc-number">8.7.</span> <span class="toc-text">异步模式之工作线程</span></a><ol class="toc-child"><li class="toc-item toc-level-4"><a class="toc-link" href="#%E9%A5%A5%E9%A5%BF%E9%97%AE%E9%A2%98"><span class="toc-number">8.7.1.</span> <span class="toc-text">饥饿问题</span></a></li><li class="toc-item toc-level-4"><a class="toc-link" href="#%E5%88%9B%E5%BB%BA%E5%A4%9A%E5%B0%91%E7%BA%BF%E7%A8%8B%E6%B1%A0%E5%90%88%E9%80%82"><span class="toc-number">8.7.2.</span> <span class="toc-text">创建多少线程池合适</span></a></li></ol></li></ol></li></ol>
    </nav>
  </div>
</aside>

<main class="main" role="main">
  <div class="content">
  <article id="post-java并发编程" class="article article-type-post" itemscope itemtype="http://schema.org/BlogPosting">
    
    <div class="article-header">
      
        
  
    <h1 class="article-title" itemprop="name">
      JVM并发编程
    </h1>
  

      
      <div class="article-meta">
        <span class="article-date">
    <i class="icon icon-calendar-check"></i>
	<a href="/Skyung/2022/04/20/java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/" class="article-date">
	  <time datetime="2022-04-19T16:00:00.000Z" itemprop="datePublished">2022-04-20</time>
	</a>
</span>
        
  <span class="article-category">
    <i class="icon icon-folder"></i>
    <a class="article-category-link" href="/Skyung/categories/%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0/">学习笔记</a>
  </span>

        
  <span class="article-tag">
    <i class="icon icon-tags"></i>
	<a class="article-tag-link-link" href="/Skyung/tags/java/" rel="tag">java</a>, <a class="article-tag-link-link" href="/Skyung/tags/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/" rel="tag">并发编程</a>
  </span>


        

        <span class="post-comment"><i class="icon icon-comment"></i> <a href="/Skyung/2022/04/20/java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/#comments" class="article-comment-link">评论</a></span>
        
      </div>
    </div>
    <div class="article-entry marked-body" itemprop="articleBody">
      
        <p>笔记参考黑马程序员java并发编程教程，<a target="_blank" rel="noopener" href="https://www.bilibili.com/video/BV16J411h7Rd?p=2&amp;share_source=copy_web">https://www.bilibili.com/video/BV16J411h7Rd?p=2&amp;share_source=copy_web</a></p>
<h2 id="概念"><a href="#概念" class="headerlink" title="概念"></a>概念</h2><h3 id="进程与线程"><a href="#进程与线程" class="headerlink" title="进程与线程"></a>进程与线程</h3><h4 id="进程"><a href="#进程" class="headerlink" title="进程"></a>进程</h4><ul>
<li>程序由指令和数据组成，但这些指令要运行，数据要读写，就必须将指令加载至 CPU，数据加载至内存。在指令运行过程中还需要用到磁盘、网络等设备。进程就是用来加载指令、管理内存、管理 IO 的。</li>
<li>当一个程序被运行，从磁盘加载这个程序的代码至内存，这时就开启了一个进程。</li>
<li>进程就可以视为程序的一个实例。大部分程序可以同时运行多个实例进程（例如记事本、画图、浏览器等），也有的程序只能启动一个实例进程（例如网易云音乐、360 安全卫士等）</li>
</ul>
<h4 id="线程"><a href="#线程" class="headerlink" title="线程"></a>线程</h4><ul>
<li>一个进程之内可以分为一到多个线程。</li>
<li>一个线程就是一个指令流，将指令流中的一条条指令以一定的顺序交给 CPU 执行 。</li>
<li>Java 中，线程作为小调度单位，进程作为资源分配的小单位。 在 windows 中进程是不活动的，只是作为线程的容器</li>
</ul>
<h4 id="二者对比"><a href="#二者对比" class="headerlink" title="二者对比"></a>二者对比</h4><ul>
<li>进程基本上相互独立的，而线程存在于进程内，是进程的一个子集</li>
<li>进程拥有共享的资源，如内存空间等，供其内部的线程共享<ul>
<li>进程间通信较为复杂，同一台计算机的进程通信称为 IPC（Inter-process communication）</li>
<li>不同计算机之间的进程通信，需要通过网络，并遵守共同的协议，例如 HTTP</li>
</ul>
</li>
<li>线程通信相对简单，因为它们共享进程内的内存，多个线程可以访问同一个共享变量</li>
<li>线程更轻量，线程上下文切换成本一般上要比进程上下文切换低</li>
</ul>
<h4 id="并行和并发"><a href="#并行和并发" class="headerlink" title="并行和并发"></a>并行和并发</h4><p>单核 cpu 下，线程串行执行 的。操作系统中有一个组件叫做任务调度器，将 cpu 的时间片（windows下时间片最小约为 15 毫秒）分给不同的程序使用，只是由于 cpu 在线程间（时间片很短）的切换非常快，人感觉是同时运行的 。总结： 微观串行，宏观并行 。</p>
<p>将这种线程轮流使用 CPU 的做法称为并发， concurrent。</p>
<p>并发是一个CPU在不同的时间去不同线程中执行指令。</p>
<p>并行是多个CPU同时处理不同的线程。</p>
<ul>
<li>并发（concurrent）是同一时间<strong>应对</strong>（dealing with）多件事情的能力</li>
<li>并行（parallel）是同一时间<strong>动手做</strong>（doing）多件事情的能力</li>
</ul>
<h4 id="应用"><a href="#应用" class="headerlink" title="应用"></a>应用</h4><p><strong>应用之异步调用</strong></p>
<p>以调用方角度来讲，如果需要等待结果返回，才能继续运行就是同步；不需要等待结果返回，就能继续运行就是异步。</p>
<p>设计</p>
<ul>
<li>比如说读取磁盘文件时，假设读取操作花费了 5 秒钟，如果没有线程调度机制，这 5 秒 cpu 什么都做不了，其它代码都得暂停，单开线程读取</li>
<li>tomcat 的异步servlet也是类似的目的，让用户线程处理耗时较长的操作，避免阻塞</li>
</ul>
<p>结论</p>
<ol>
<li>单核 cpu 下，多线程不能实际提高程序运行效率，只是为了能够在不同的任务之间切换，不同线程轮流使用 cpu ，不至于一个线程总占用 cpu，别的线程没法干活</li>
<li>多核 cpu 可以并行跑多个线程，但能否提高程序运行效率还是要分情况的<ul>
<li>有些任务，经过精心设计，将任务拆分，并行执行，当然可以提高程序的运行效率。但不是所有计算任务都能拆分（参考后文的【阿姆达尔定律】）</li>
<li>也不是所有任务都需要拆分，任务的目的如果不同，谈拆分和效率没啥意义</li>
</ul>
</li>
<li>IO 操作不占用 cpu，只是我们一般拷贝文件使用的是【阻塞 IO】，这时相当于线程虽然不用 cpu，但需要一 直等待 IO 结束，没能充分利用线程。所以才有后面的【非阻塞 IO】和【异步 IO】优化</li>
</ol>
<h2 id="JMM"><a href="#JMM" class="headerlink" title="JMM"></a>JMM</h2><p>java内存模型（JMM） 定义了一套在多线程读写共享数据时（成员变量、数组）时，对数据的可见性、有序 性、和原子性的规则和保障。</p>
<ul>
<li>原子性 - 保证指令不会受到线程上下文切换的影响 </li>
<li>可见性 - 保证指令不会受 cpu 缓存的影响</li>
<li>有序性 - 保证指令不会受 cpu 指令并行优化的影响</li>
</ul>
<h3 id="原子性"><a href="#原子性" class="headerlink" title="原子性"></a>原子性</h3><p>即一个操作或者多个操作，要么全部执行并且执行的过程不会被任何因素打断，要么就都不执行。原子性就像数据库里面的事务一样。</p>
<h4 id="问题分析"><a href="#问题分析" class="headerlink" title="问题分析"></a>问题分析</h4><p>两个线程对初始值为0的静态变量一个做自增，一个做自减，各做 5000 次。以上的结果可能是正数、负数、零。</p>
<p>因为 Java 中对静态变量的自增，自减并不是原子操作。</p>
<p>例如对于 i++ 而言（i 为静态变量），实际会产生如下的 JVM 字节码指令：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">getstatic i // 获取静态变量i的值</span><br><span class="line">iconst_1 // 准备常量1</span><br><span class="line">iadd // 加法</span><br><span class="line">putstatic i // 将修改后的值存入静态变量i</span><br></pre></td></tr></table></figure>

<p>而 Java 的内存模型如下，完成静态变量的自增，自减需要在主存和线程内存中进行数据交换：</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220314161808081.png" alt="image-20220314161808081"></p>
<p>但多线程下代码可能交错运行</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">// 假设i的初始值为0，出现负数的情况：</span><br><span class="line">getstatic i // 线程1-获取静态变量i的值 线程内i=0</span><br><span class="line">getstatic i // 线程2-获取静态变量i的值 线程内i=0</span><br><span class="line">iconst_1 // 线程1-准备常量1</span><br><span class="line">iadd // 线程1-自增 线程内i=1</span><br><span class="line">putstatic i // 线程1-将修改后的值存入静态变量i 静态变量i=1</span><br><span class="line">iconst_1 // 线程2-准备常量1</span><br><span class="line">isub // 线程2-自减 线程内i=-1</span><br><span class="line">putstatic i // 线程2-将修改后的值存入静态变量i 静态变量i=-1</span><br></pre></td></tr></table></figure>

<h4 id="解决方法"><a href="#解决方法" class="headerlink" title="解决方法"></a>解决方法</h4><p>synchronized （同步关键字）语法</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">synchronized( 对象 ) &#123;</span><br><span class="line">	要作为原子操作代码</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>用 synchronized 解决并发问题：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"><span class="keyword">static</span> <span class="type">Object</span> <span class="variable">obj</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">    <span class="type">Thread</span> <span class="variable">t1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">    	<span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">0</span>; j &lt; <span class="number">5000</span>; j++) &#123;</span><br><span class="line">    		<span class="keyword">synchronized</span> (obj) &#123;</span><br><span class="line">    			i++;</span><br><span class="line">    		&#125;</span><br><span class="line">    	&#125;</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="type">Thread</span> <span class="variable">t2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">0</span>; j &lt; <span class="number">5000</span>; j++) &#123;</span><br><span class="line">            <span class="keyword">synchronized</span> (obj) &#123;</span><br><span class="line">                i--;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">    t1.start();</span><br><span class="line">    t2.start();</span><br><span class="line">    t1.join();</span><br><span class="line">    t2.join();</span><br><span class="line">    System.out.println(i);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="可见性"><a href="#可见性" class="headerlink" title="可见性"></a>可见性</h3><h4 id="问题分析-1"><a href="#问题分析-1" class="headerlink" title="问题分析"></a>问题分析</h4><p>下列main线程对run 变量的修改对于t线程不可见，导致了 t 线程无法停止</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="type">boolean</span> <span class="variable">run</span> <span class="operator">=</span> <span class="literal">true</span>;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">	<span class="type">Thread</span> <span class="variable">t</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(()-&gt;&#123;</span><br><span class="line">		<span class="keyword">while</span>(run)&#123;</span><br><span class="line">			<span class="comment">// ....</span></span><br><span class="line">		&#125;</span><br><span class="line">	&#125;);</span><br><span class="line">	t.start();</span><br><span class="line">	Thread.sleep(<span class="number">1000</span>);</span><br><span class="line">	run = <span class="literal">false</span>; <span class="comment">// 线程t不会如预想的停下来</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>现象的原因</p>
<ol>
<li>初始状态， t 线程刚开始从主内存读取了run的值到工作内存。</li>
<li>因为 t 线程要频繁从主内存中读取run 的值，JIT 编译器会将 run 的值缓存至自己工作内存中的高速缓存中，减少对主存中 run 的访问，提高效率</li>
<li>1 秒之后，main 线程修改了 run 的值，并同步至主存，而 t 是从自己工作内存中的高速缓存中读取这个变量的值，结果永远是旧值。</li>
</ol>
<h4 id="解决方法-1"><a href="#解决方法-1" class="headerlink" title="解决方法"></a>解决方法</h4><p>volatile（易变关键字）</p>
<p>用来修饰成员变量和静态成员变量，他可以避免线程从自己的工作缓存中查找变量的值，必须到主存中获取它的值，线程操作volatile 变量都是直接操作主存。它保证的是在多个线程之间，一个线程对 volatile 变量的修改对另一 个线程可见， 不能保证原子性。</p>
<blockquote>
<p>注意 synchronized 语句块既可以保证代码块的原子性，也同时保证代码块内变量的可见性。但缺点是 synchronized是属于重量级操作，性能相对更低。</p>
<p>如果在前面示例的死循环中加入 System.out.println() 会发现即使不加 volatile 修饰符，线程 t 也 能正确看到对 run 变量的修改了。println() 自带synchronized修饰，破坏了JIT 编译器优化，从主存中读取数据，所以synchronized 性能低。</p>
</blockquote>
<h3 id="有序性"><a href="#有序性" class="headerlink" title="有序性"></a>有序性</h3><h4 id="原理之指令重排"><a href="#原理之指令重排" class="headerlink" title="原理之指令重排"></a>原理之指令重排</h4><p><strong>CPU指令的重排序</strong></p>
<p>CPU指令还可以再划分成一个个更小的阶段，例如，每条指令都可以分为： 取指令 - 指令译码 - 执行指令 - 内存访问 - 数据 写回这 5 个阶段</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220415105816401.png" alt="image-20220415105816401"></p>
<blockquote>
<p>instruction fetch (IF) </p>
<p>instruction decode (ID) </p>
<p>execute (EX) </p>
<p>memory access (MEM) </p>
<p>register write back (WB)</p>
</blockquote>
<p>CPU 支持多级指令流水线，例如支持同时执行 取指令 - 指令译码 - 执行指令 - 内存访问 - 数据写回 的处理器，就可以称之为五级指令流水线。这时 CPU 可以在一个时钟周期内，同时运行五条指令的不同阶段（相当于一 条执行时间最长的复杂指令），IPC = 1，本质上，流水线技术并不能缩短单条指令的执行时间，但它变相地提高了 指令地吞吐率。</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220415110242622.png" alt="image-20220415110242622"></p>
<p>在不改变程序结果的前提下，这些指令的各个阶段可以通过重排序和组合来实现指令级并行，这一技术在 80’s 中 叶到 90’s 中叶占据了计算架构的重要地位。</p>
<p>指令重排的前提是，重排指令不能影响结果</p>
<p><strong>jvm层面也存在指令的重排序</strong>，例如</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">static int i;</span><br><span class="line">static int j;</span><br><span class="line">// 在某个线程内执行如下赋值操作</span><br><span class="line">i = ...; // 较为耗时的操作</span><br><span class="line">j = ...;</span><br></pre></td></tr></table></figure>

<p>可以看到，至于是先执行 i 还是 先执行 j ，对最终的结果不会产生影响。所以上面代码真正执行时，既可以是</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">i = ...; // 较为耗时的操作</span><br><span class="line">j = ...;</span><br></pre></td></tr></table></figure>

<p>也可以是</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">j = ...;</span><br><span class="line">i = ...; // 较为耗时的操作</span><br></pre></td></tr></table></figure>

<p>这种特性称之为『指令重排』，多线程下『指令重排』会影响正确性</p>
<h4 id="问题分析-2"><a href="#问题分析-2" class="headerlink" title="问题分析"></a>问题分析</h4><h5 id="double-checked-locking问题"><a href="#double-checked-locking问题" class="headerlink" title="double-checked locking问题"></a>double-checked locking问题</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">Singleton</span> &#123;</span><br><span class="line">	<span class="keyword">private</span> <span class="title function_">Singleton</span><span class="params">()</span> &#123; &#125;</span><br><span class="line">	<span class="keyword">private</span> <span class="keyword">static</span> <span class="type">Singleton</span> <span class="variable">INSTANCE</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">static</span> Singleton <span class="title function_">getInstance</span><span class="params">()</span> &#123;</span><br><span class="line">		<span class="comment">// 实例没创建，才会进入内部的 synchronized代码块</span></span><br><span class="line">		<span class="keyword">if</span> (INSTANCE == <span class="literal">null</span>) &#123;<span class="comment">//INSTANCE在synchronized外，会存在线程安全问题</span></span><br><span class="line">			<span class="keyword">synchronized</span> (Singleton.class) &#123;</span><br><span class="line">				<span class="comment">// 也许有其它线程已经创建实例，所以再判断一次</span></span><br><span class="line">				<span class="keyword">if</span> (INSTANCE == <span class="literal">null</span>) &#123;</span><br><span class="line">					INSTANCE = <span class="keyword">new</span> <span class="title class_">Singleton</span>();</span><br><span class="line">				&#125;</span><br><span class="line">			&#125;</span><br><span class="line">		&#125;</span><br><span class="line">		<span class="keyword">return</span> INSTANCE;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>但在多线程环境下，上面的代码是有问题的，INSTANCE = new Singleton() 对应的字节码为：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">17: new #2 // class cn/itcast/jvm/t4/Singleton</span><br><span class="line">20: dup</span><br><span class="line">21: invokespecial #3 // Method &quot;&lt;init&gt;&quot;:()V</span><br><span class="line">14: putstatic #4 // Field</span><br><span class="line">INSTANCE:Lcn/itcast/jvm/t4/Singleton;</span><br></pre></td></tr></table></figure>

<p>其中：</p>
<ul>
<li><p>17 表示创建对象，将对象引用入栈 // new Singleton </p>
</li>
<li><p>20 表示复制一份对象引用 // 引用地址 </p>
</li>
<li><p>21 表示利用一个对象引用，调用构造方法 </p>
</li>
<li><p>24 表示利用一个对象引用，赋值给 static INSTANCE</p>
</li>
</ul>
<p>其中 21 24 两步的顺序不是固定的，也许 jvm 会优化为：先将引用地址赋值给 INSTANCE 变量后，再执行构造方法</p>
<p>这时 t1 还未完全将构造方法执行完毕，如果在构造方法中要执行很多初始化操作，那么 t2 拿到的是将是一个未初始化完毕的单例</p>
<p><strong>对 INSTANCE 使用 volatile 修饰即可，可以禁用指令重排</strong>，但要注意在 JDK 5 以上的版本的 volatile 才会真正有效</p>
<blockquote>
<p>插一句，如果实现了序列化，反序列化会破坏单例，需要加上</p>
<p>public Object readResovle(){return INSTANCE}</p>
</blockquote>
<h5 id="指令重排问题"><a href="#指令重排问题" class="headerlink" title="指令重排问题"></a>指令重排问题</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="variable">num</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"><span class="type">boolean</span> <span class="variable">ready</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line"><span class="comment">// 线程1 执行此方法</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">actor1</span><span class="params">(I_Result r)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span>(ready) &#123;</span><br><span class="line">    	r.r1 = num + num;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    	r.r1 = <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 线程2 执行此方法</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">actor2</span><span class="params">(I_Result r)</span> &#123;</span><br><span class="line">    num = <span class="number">2</span>;</span><br><span class="line">    ready = <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>结果有可能是0：线程2 执行 ready = true，切换到线程1，进入 if 分支，相加为 0，再切回线程2执行 num = 2。</p>
<p>这种现象叫做指令重排，是 JIT 编译器在运行时的一些优化，这个现象需要通过大量测试才能复现：</p>
<p>借助 java 并发压测工具 jcstress <a target="_blank" rel="noopener" href="https://wiki.openjdk.java.net/display/CodeTools/jcstress">https://wiki.openjdk.java.net/display/CodeTools/jcstress</a></p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">mvn archetype:generate -DinteractiveMode=false -</span><br><span class="line">DarchetypeGroupId=org.openjdk.jcstress -DarchetypeArtifactId=jcstress-java-test-archetype -DgroupId=org.sample -DartifactId=test -Dversion=1.0</span><br></pre></td></tr></table></figure>

<p>创建 maven 项目，提供如下测试类</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@JCStressTest</span></span><br><span class="line"><span class="meta">@Outcome(id = &#123;&quot;1&quot;, &quot;4&quot;&#125;, expect = Expect.ACCEPTABLE, desc = &quot;ok&quot;)</span></span><br><span class="line"><span class="meta">@Outcome(id = &quot;0&quot;, expect = Expect.ACCEPTABLE_INTERESTING, desc = &quot;!!!!&quot;)</span></span><br><span class="line"><span class="meta">@State</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ConcurrencyTest</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">num</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="type">boolean</span> <span class="variable">ready</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line">    <span class="meta">@Actor</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">actor1</span><span class="params">(I_Result r)</span> &#123;</span><br><span class="line">    	<span class="keyword">if</span>(ready) &#123;</span><br><span class="line">    		r.r1 = num + num;</span><br><span class="line">    	&#125; <span class="keyword">else</span> &#123;</span><br><span class="line">   		 r.r1 = <span class="number">1</span>;</span><br><span class="line">    	&#125;</span><br><span class="line">    &#125;</span><br><span class="line">	<span class="meta">@Actor</span></span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">actor2</span><span class="params">(I_Result r)</span> &#123;</span><br><span class="line">		num = <span class="number">2</span>;</span><br><span class="line">		ready = <span class="literal">true</span>;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>执行</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">mvn clean install</span><br><span class="line">java -jar target/jcstress.jar</span><br></pre></td></tr></table></figure>

<p>会输结果，摘录其中一次结果：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">*** INTERESTING tests</span><br><span class="line">Some interesting behaviors observed. This is for the plain curiosity.</span><br><span class="line">2 matching test results.</span><br><span class="line">[OK] test.ConcurrencyTest</span><br><span class="line">(JVM args: [-XX:-TieredCompilation])</span><br><span class="line">Observed state Occurrences Expectation Interpretation</span><br><span class="line">0 	1,729 	ACCEPTABLE_INTERESTING !!!!</span><br><span class="line">1 	42,617,915 	ACCEPTABLE ok</span><br><span class="line">4 	5,146,627 	ACCEPTABLE ok</span><br><span class="line">[OK] test.ConcurrencyTest</span><br><span class="line">(JVM args: [])</span><br><span class="line">Observed state Occurrences Expectation Interpretation</span><br><span class="line">0 	1,652 	ACCEPTABLE_INTERESTING !!!!</span><br><span class="line">1 	46,460,657 	ACCEPTABLE ok</span><br><span class="line">4 	4,571,072 	ACCEPTABLE ok</span><br></pre></td></tr></table></figure>

<p>可以看到，出现结果为 0 的情况有 638 次，虽然次数相对很少，但毕竟是出现了。</p>
<h4 id="解决方法-2"><a href="#解决方法-2" class="headerlink" title="解决方法"></a>解决方法</h4><p>volatile 修饰的变量，可以禁用指令重排</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">volatile boolean ready = false;</span><br></pre></td></tr></table></figure>

<p>这是并发压力测试的结果为</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">*** INTERESTING tests</span><br><span class="line">Some interesting behaviors observed. This is for the plain curiosity.</span><br><span class="line">0 matching test results.</span><br><span class="line"></span><br></pre></td></tr></table></figure>

<h4 id="原理之volatile"><a href="#原理之volatile" class="headerlink" title="原理之volatile"></a>原理之volatile</h4><p>volatile 的底层实现原理是内存屏障，Memory Barrier（Memory Fence）</p>
<ul>
<li>对 volatile 变量的写指令后会加入写屏障 </li>
<li>对 volatile 变量的读指令前会加入读屏障</li>
</ul>
<p>更底层是读写变量时使用 lock 指令来多核 CPU 之间的可见性与有序性</p>
<p><strong>如何保证可见性</strong></p>
<ul>
<li><p>写屏障（sfence）保证在该屏障之前的，对共享变量的改动，都同步到主存当中</p>
</li>
<li><p>而读屏障（lfence）保证在该屏障之后，对共享变量的读取，加载的是主存中最新数据</p>
</li>
</ul>
<p><strong>如何保证有序性</strong></p>
<ul>
<li>写屏障会确保指令重排序时，不会将写屏障之前的代码排在写屏障之后</li>
<li>读屏障会确保指令重排序时，不会将读屏障之后的代码排在读屏障之前</li>
</ul>
<p>还是那句话，不能解决指令交错：</p>
<ul>
<li><p>写屏障仅仅是保证之后的读能够读到最新的结果，但不能保证读跑到它前面去</p>
</li>
<li><p>而有序性的保证也只是保证了本线程内相关代码不被重排序</p>
</li>
</ul>
<p>例如多个线程的i++</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220415131825339.png" alt="image-20220415131825339"></p>
<p>所以 volatile只能保证本线程的相关代码不会重排，也就是保证可见和有序，其他线程仍会交替运行，不能保证原子性。</p>
<h4 id="happens-before"><a href="#happens-before" class="headerlink" title="happens-before"></a>happens-before</h4><p>happens-before 规定了哪些写操作对其它线程的读操作可见，它是可见性与有序性的一套规则总结。</p>
<p>抛开以下 happens-before 规则，JMM 并不能保证一个线程对共享变量的写，对于其它线程对该共享变量的读可见</p>
<ul>
<li><p>线程解锁 m 之前对变量的写，对于接下来对 m 加锁的其它线程对该变量的读可见</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="type">int</span> x;</span><br><span class="line"><span class="keyword">static</span> <span class="type">Object</span> <span class="variable">m</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Thread</span>(()-&gt;&#123;</span><br><span class="line">	<span class="keyword">synchronized</span>(m) &#123;</span><br><span class="line">		x = <span class="number">10</span>;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;,<span class="string">&quot;t1&quot;</span>).start();</span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Thread</span>(()-&gt;&#123;</span><br><span class="line">	<span class="keyword">synchronized</span>(m) &#123;</span><br><span class="line">		System.out.println(x);</span><br><span class="line">	&#125;</span><br><span class="line">&#125;,<span class="string">&quot;t2&quot;</span>).start();</span><br></pre></td></tr></table></figure></li>
<li><p>线程对 volatile 变量的写，对接下来其它线程对该变量的读可见</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">volatile</span> <span class="keyword">static</span> <span class="type">int</span> x;</span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Thread</span>(()-&gt;&#123;</span><br><span class="line">	x = <span class="number">10</span>;</span><br><span class="line">&#125;,<span class="string">&quot;t1&quot;</span>).start();</span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Thread</span>(()-&gt;&#123;</span><br><span class="line">	System.out.println(x);</span><br><span class="line">&#125;,<span class="string">&quot;t2&quot;</span>).start();</span><br></pre></td></tr></table></figure></li>
<li><p>线程 start 前对变量的写，对该线程开始后对该变量的读可见</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="type">int</span> x;</span><br><span class="line">x = <span class="number">10</span>;</span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Thread</span>(()-&gt;&#123;</span><br><span class="line">	System.out.println(x);</span><br><span class="line">&#125;,<span class="string">&quot;t2&quot;</span>).start();</span><br></pre></td></tr></table></figure></li>
<li><p>线程结束前对变量的写，对其它线程得知它结束后的读可见（比如其它线程调用 t1.isAlive() 或 t1.join()等待它结束）</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="type">int</span> x;</span><br><span class="line"><span class="type">Thread</span> <span class="variable">t1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(()-&gt;&#123;</span><br><span class="line">	x = <span class="number">10</span>;</span><br><span class="line">&#125;,<span class="string">&quot;t1&quot;</span>);</span><br><span class="line">t1.start();</span><br><span class="line">t1.join();</span><br><span class="line">System.out.println(x);</span><br></pre></td></tr></table></figure></li>
<li><p>线程 t1 打断 t2（interrupt）前对变量的写，对于其他线程得知 t2 被打断后对变量的读可见（通 过t2.interrupted 或 t2.isInterrupted）</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="type">int</span> x;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="type">Thread</span> <span class="variable">t2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(()-&gt;&#123;</span><br><span class="line">        <span class="keyword">while</span>(<span class="literal">true</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span>(Thread.currentThread().isInterrupted()) &#123;</span><br><span class="line">                System.out.println(x);<span class="comment">//t2被打断后,能读到x的最新值</span></span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;,<span class="string">&quot;t2&quot;</span>);</span><br><span class="line">    t2.start();</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(()-&gt;&#123;</span><br><span class="line">    	<span class="keyword">try</span> &#123;</span><br><span class="line">    		Thread.sleep(<span class="number">1000</span>);</span><br><span class="line">    	&#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">    		e.printStackTrace();</span><br><span class="line">    &#125;</span><br><span class="line">    	x = <span class="number">10</span>;</span><br><span class="line">    	t2.interrupt();<span class="comment">//在打断t2线程前，对x进行了写</span></span><br><span class="line">    &#125;,<span class="string">&quot;t1&quot;</span>).start();</span><br><span class="line">    <span class="keyword">while</span>(!t2.isInterrupted()) &#123;</span><br><span class="line">    	Thread.yield();</span><br><span class="line">    &#125;</span><br><span class="line">    System.out.println(x);<span class="comment">//主线程得知t2被打断后,能读到x的最新值</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li>
<li><p>对变量默认值（0，false，null）的写，对其它线程对该变量的读可见</p>
</li>
<li><p>具有传递性，如果 x hb-&gt; y 并且 y hb-&gt; z 那么有 x hb-&gt; z</p>
<blockquote>
<p>变量都是指成员变量或静态成员变量，也就是共享的变量</p>
</blockquote>
</li>
</ul>
<h2 id="java线程"><a href="#java线程" class="headerlink" title="java线程"></a>java线程</h2><h3 id="创建和运行线程"><a href="#创建和运行线程" class="headerlink" title="创建和运行线程"></a>创建和运行线程</h3><p><strong>方法一：Thread创建线程</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Thread</span> <span class="variable">t</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>() &#123;</span><br><span class="line"> 	<span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line"> 		<span class="comment">// 要执行的任务</span></span><br><span class="line"> 	&#125;</span><br><span class="line">&#125;;</span><br><span class="line">t.start();</span><br></pre></td></tr></table></figure>

<p><strong>方法二，使用 Runnable 配合 Thread，（推荐）</strong></p>
<p>把【线程】和【任务】（要执行的代码）分开，Thread 代表线程， Runnable 可运行的任务（线程要执行的代码）</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Runnable</span> <span class="variable">runnable</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Runnable</span>() &#123;</span><br><span class="line">     <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span>&#123;</span><br><span class="line">     	<span class="comment">// 要执行的任务</span></span><br><span class="line">     &#125;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="type">Thread</span> <span class="variable">t</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>( runnable );</span><br><span class="line">t.start(); </span><br></pre></td></tr></table></figure>

<p>Java 8 以后可以使用lambda 精简代码</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Runnable</span> <span class="variable">r</span> <span class="operator">=</span> () -&gt; &#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;Runnable running&quot;</span>);</span><br><span class="line">&#125;;</span><br><span class="line"><span class="type">Thread</span> <span class="variable">t</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(r);</span><br><span class="line">t.start();</span><br></pre></td></tr></table></figure>

<p>方法2 是把线程和任务分开了，用 Runnable 更容易与线程池等高级 API 配合 用 Runnable 让任务类脱离了 Thread 继承体系，更灵活<strong>（组合优于继承）</strong></p>
<p><strong>方法三：FutureTask 配合 Thread</strong></p>
<p>FutureTask 能够接收 Callable 类型的参数，用来处理有返回结果的情况</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 创建任务对象，需要传入一个Callable对象</span></span><br><span class="line">FutureTask&lt;Integer&gt; task = <span class="keyword">new</span> <span class="title class_">FutureTask</span>&lt;Integer&gt;(<span class="keyword">new</span> <span class="title class_">Callable</span>&lt;Integer&gt;() &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Integer <span class="title function_">call</span><span class="params">()</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;线程执行!&quot;</span>);</span><br><span class="line">        Thread.sleep(<span class="number">1000</span>);</span><br><span class="line">        <span class="keyword">return</span> <span class="number">100</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;);</span><br><span class="line"><span class="comment">// 参数1 是任务对象; 参数2 是线程名字，推荐</span></span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Thread</span>(task3, <span class="string">&quot;t3&quot;</span>).start();</span><br><span class="line"><span class="comment">// 主线程阻塞，同步等待 task 执行完毕的结果</span></span><br><span class="line"><span class="type">Integer</span> <span class="variable">result</span> <span class="operator">=</span> task3.get();</span><br><span class="line">log.debug(<span class="string">&quot;结果是:&#123;&#125;&quot;</span>, result);</span><br></pre></td></tr></table></figure>

<p><strong>Thread 与 Runnable 的关系</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Thread</span> <span class="keyword">implements</span> <span class="title class_">Runnable</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="title function_">Thread</span><span class="params">(ThreadGroup g, Runnable target, String name,</span></span><br><span class="line"><span class="params">                       <span class="type">long</span> stackSize, AccessControlContext acc,</span></span><br><span class="line"><span class="params">                       <span class="type">boolean</span> inheritThreadLocals)</span> &#123;</span><br><span class="line">        <span class="comment">//...</span></span><br><span class="line">        <span class="built_in">this</span>.target = target;   </span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (target != <span class="literal">null</span>) &#123;</span><br><span class="line">            target.run();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="查看进程和线程"><a href="#查看进程和线程" class="headerlink" title="查看进程和线程"></a>查看进程和线程</h3><p>windous</p>
<ul>
<li>任务管理器可以查看进程和线程数，也可以用来杀死进程</li>
<li>tasklist 查看进程</li>
<li>taskkill 杀死进程</li>
</ul>
<p>linux</p>
<ul>
<li>ps -fe 查看所有进程</li>
<li>ps -fT -p <PID> 查看某个进程（PID）的所有线程</li>
<li>kill 杀死进程</li>
<li>top 按大写 H 切换是否显示线程</li>
<li>top -H -p <PID> 查看某个进程（PID）的所有线程</li>
</ul>
<p>java</p>
<ul>
<li>jps 命令查看所有 Java 进程</li>
<li>jstack <PID> 查看某个 Java 进程（PID）的所有线程状态</li>
<li>jconsole 来查看某个 Java 进程中线程的运行情况（图形界面）</li>
</ul>
<p>jconsole 远程监控配置</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">java -Djava.rmi.server.hostname=ip地址 -Dcom.sun.management.jmxremote -</span><br><span class="line">Dcom.sun.management.jmxremote.port=连接端口 -Dcom.sun.management.jmxremote.ssl=是否安全连接 -</span><br><span class="line">Dcom.sun.management.jmxremote.authenticate=是否认证 java类</span><br></pre></td></tr></table></figure>

<p>如果要认证访问，还需要做如下步骤</p>
<p>复制 jmxremote.password 文件；修改 jmxremote.password 和 jmxremote.access 文件的权限为 600 即文件所有者可读写；连接时填入 controlRole（用户名），R&amp;D（密码）</p>
<h3 id="线程运行原理"><a href="#线程运行原理" class="headerlink" title="线程运行原理"></a>线程运行原理</h3><p>每个线程启动后，虚拟机就会为其分配一块栈内存。每个栈由多个栈帧（Frame）组成，对应着每次方法调用时所占用的内存每个线程只能有一个活动栈帧，对应着当前正在执行的那个方法。</p>
<p><strong>线程上下文切换（Thread Context Switch）</strong></p>
<p>因为以下一些原因导致 cpu 不再执行当前的线程，转而执行另一个线程的代码</p>
<ul>
<li><p>线程的 cpu 时间片用完</p>
</li>
<li><p>垃圾回收</p>
</li>
<li><p>有更高优先级的线程需要运行</p>
</li>
<li><p>线程自己调用了 sleep、yield、wait、join、park、synchronized、lock 等方法</p>
</li>
</ul>
<p>当 Context Switch 发生时，需要由操作系统保存当前线程的状态，并恢复另一个线程的状态，Java 中对应的概念就是程序计数器（Program Counter Register），它的作用是记住下一条 jvm 指令的执行地址，是线程私有的</p>
<ul>
<li>状态包括程序计数器、虚拟机栈中每个栈帧的信息，如局部变量、操作数栈、返回地址等</li>
<li>Context Switch 频繁发生会影响性能</li>
</ul>
<h3 id="常见方法"><a href="#常见方法" class="headerlink" title="常见方法"></a>常见方法</h3><table>
<thead>
<tr>
<th>方法名</th>
<th>static</th>
<th>功能</th>
<th>注意</th>
</tr>
</thead>
<tbody><tr>
<td>start()</td>
<td></td>
<td>启动一个新线程，在新的线程运行 run 方法中的代码</td>
<td>start 方法只是让线程进入就绪，里面代码不一定立刻 运行（CPU 的时间片还没分给它）。每个线程对象的 start方法只能调用一次，如果调用了多次会出现 IllegalThreadStateException</td>
</tr>
<tr>
<td>run()</td>
<td></td>
<td>新线程启动后会调用的方法</td>
<td>如果在构造 Thread对象时传递了 Runnable 参数，则线程启动后会调用 Runnable 中的 run 方法，否则默认不执行任何操作。但可以创建 Thread 的子类对象， 来覆盖默认行为</td>
</tr>
<tr>
<td>join()</td>
<td></td>
<td>等待线程运行结束</td>
<td></td>
</tr>
<tr>
<td>join(long n)</td>
<td></td>
<td>等待线程运行结束,最多等待 n  毫秒</td>
<td></td>
</tr>
<tr>
<td>getId()</td>
<td></td>
<td>获取线程长整型的 id</td>
<td>id 唯一</td>
</tr>
<tr>
<td>getName()</td>
<td></td>
<td>获取线程名</td>
<td></td>
</tr>
<tr>
<td>setName(String)</td>
<td></td>
<td>修改线程名</td>
<td></td>
</tr>
<tr>
<td>getPriority(</td>
<td></td>
<td>获取线程优先级</td>
<td></td>
</tr>
<tr>
<td>setPriority(int)</td>
<td></td>
<td>修改线程优先级</td>
<td>java中规定线程优先级是1~10 的整数，较大的优先级能提高该线程被 CPU 调度的机率</td>
</tr>
<tr>
<td>getState()</td>
<td></td>
<td>获取线程状态</td>
<td>Java 中线程状态是用 6 个 enum 表示，分别为： NEW, RUNNABLE, BLOCKED, WAITING,  TIMED_WAITING, TERMINATED</td>
</tr>
<tr>
<td>isInterrupted()</td>
<td></td>
<td>判断是否被打断</td>
<td>不会清除打断标记</td>
</tr>
<tr>
<td>isAlive()</td>
<td></td>
<td>线程是否存活 （还没有运行完 毕）</td>
<td></td>
</tr>
<tr>
<td>interrupt()</td>
<td></td>
<td>打断线程</td>
<td>如果被打断线程正在 sleep，wait，join 会导致被打断 的线程抛出 InterruptedException，并清除打断标记 ；如果打断的正在运行的线程，则会设置打断标记 ；park 的线程被打断，也会设置 打断标记</td>
</tr>
<tr>
<td>interrupted()</td>
<td>static</td>
<td>判断当前线程是否被打断</td>
<td>会清除打断标记</td>
</tr>
<tr>
<td>currentThread()</td>
<td>static</td>
<td>获取当前正在执 行的线程</td>
<td></td>
</tr>
<tr>
<td>sleep(long n)</td>
<td>static</td>
<td>让当前执行的线 程休眠n毫秒， 休眠时让出 cpu  的时间片给其它 线程</td>
<td></td>
</tr>
<tr>
<td>yield()</td>
<td>static</td>
<td>提示线程调度器让出当前线程对 CPU的使用</td>
<td>主要是为了测试和调试</td>
</tr>
</tbody></table>
<h4 id="start-与-run"><a href="#start-与-run" class="headerlink" title="start 与 run"></a>start 与 run</h4><p>直接调用run是在主线程中执行了run，没有启动新的线程；使用 start 是启动新的线程，通过新的线程间接执行run中的代码</p>
<h4 id="sleep-与-yield"><a href="#sleep-与-yield" class="headerlink" title="sleep 与 yield"></a>sleep 与 yield</h4><p>sleep</p>
<ol>
<li>调用 sleep 会让当前线程从 Running 进入 Timed Waiting 状态（阻塞）</li>
<li>其它线程可以使用 interrupt 方法打断正在睡眠的线程，这时 sleep 方法会抛出 InterruptedException</li>
<li>睡眠结束后的线程未必会立刻得到执行</li>
<li>建议用 TimeUnit 的 sleep 代替 Thread 的 sleep 来获得更好的可读性</li>
</ol>
<p>yield</p>
<ol>
<li>调用 yield 会让当前线程从 Running 进入 Runnable 就绪状态，然后调度执行其它线程</li>
<li>具体的实现依赖于操作系统的任务调度器</li>
</ol>
<p>sleep的应用</p>
<p>在没有利用 cpu 来计算时，不要让 while(true) 空转浪费 cpu，这时可以使用 yield 或 sleep 来让出 cpu 的使用权给其他程序</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">while</span>(<span class="literal">true</span>) &#123;</span><br><span class="line"> 	<span class="keyword">try</span> &#123;</span><br><span class="line"> 		Thread.sleep(<span class="number">50</span>);</span><br><span class="line"> 	&#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line"> 		e.printStackTrace();</span><br><span class="line"> 	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="join方法"><a href="#join方法" class="headerlink" title="join方法"></a>join方法</h4><p>以调用方角度来讲，如果需要等待结果返回，才能继续运行就是同步，不需要等待结果返回，就能继续运行就是异步。</p>
<h4 id="interrupt方法"><a href="#interrupt方法" class="headerlink" title="interrupt方法"></a>interrupt方法</h4><p>interrupt()不能中断在运行中的线程，它只能改变中断状态而已。</p>
<p>要停止一个线程，却不能简单的使用Thread.stop()方法。Java中断机制是一种协作机制，也就是说通过中断并不能直接终止另一个线程，而需要被中断的线程自己处理中断。当调用interrupt()方法的时候，只是设置了要中断线程的中断状态，而此时被中断的线程的可以通过isInterrupted()或者是interrupted()方法判断当前线程的中断状态是否标志为中断</p>
<ul>
<li><p>打断 sleep，wait，join 的线程</p>
<p>这几个方法都会让线程进入阻塞状态，产生InterruptedException异常。打断 sleep 的线程, 会清空打断状态，以 sleep 为例</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">test1</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">	<span class="type">Thread</span> <span class="variable">t1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(()-&gt;&#123;</span><br><span class="line">		sleep(<span class="number">1</span>);</span><br><span class="line">	&#125;, <span class="string">&quot;t1&quot;</span>);</span><br><span class="line">	t1.start();</span><br><span class="line">	sleep(<span class="number">0.5</span>);</span><br><span class="line">	t1.interrupt();</span><br><span class="line">	log.debug(<span class="string">&quot; 打断状态: &#123;&#125;&quot;</span>, t1.isInterrupted());</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//输出打断状态: false   </span></span><br></pre></td></tr></table></figure></li>
<li><p>打断正常运行的线程</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">while</span>(<span class="literal">true</span>) &#123;</span><br><span class="line">    <span class="type">boolean</span> <span class="variable">interrupted</span> <span class="operator">=</span> Thread.currentThread().isInterrupted();</span><br><span class="line">    <span class="keyword">if</span>(interrupted) &#123;</span><br><span class="line">        <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>正常运行的线程在被打断后，不会停止，会继续执行。如果要让线程在被打断后停下来，需要使用打断标记来判断。</p>
</li>
<li><p>打断 park 线程</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">test3</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">	<span class="type">Thread</span> <span class="variable">t1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line"> 		log.debug(<span class="string">&quot;park...&quot;</span>);</span><br><span class="line">		LockSupport.park();</span><br><span class="line">		log.debug(<span class="string">&quot;unpark...&quot;</span>);</span><br><span class="line"> 		log.debug(<span class="string">&quot;打断状态：&#123;&#125;&quot;</span>, 	Thread.currentThread().isInterrupted());</span><br><span class="line"> 		&#125;, <span class="string">&quot;t1&quot;</span>);</span><br><span class="line"> 	t1.start();</span><br><span class="line"> 	sleep(<span class="number">0.5</span>);</span><br><span class="line"> 	t1.interrupt();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>打断 park 线程, 会让park线程继续运行，打断状态为true，并且park失效。只需要添加打断，park就会重新起作用</p>
</li>
</ul>
<h4 id="不推荐的方法"><a href="#不推荐的方法" class="headerlink" title="不推荐的方法"></a>不推荐的方法</h4><p>stop() 停止线程运行，可能造成共享资源无法被释放，其他线程无法使用这些共享资源</p>
<p>suspend() 挂起（暂停）线程运行</p>
<p>resume() 挂起（暂停）线程运行</p>
<h3 id="主线程和守护线程"><a href="#主线程和守护线程" class="headerlink" title="主线程和守护线程"></a>主线程和守护线程</h3><p>主线程需要等待所有线程都运行结束，才会结束</p>
<p>守护线程，只要其它非守护线程运行结束了，即使守护线程的代码没有执行完，也会强制结束。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">t1.setDaemon(<span class="literal">true</span>);<span class="comment">//设置线程为守护线程</span></span><br></pre></td></tr></table></figure>

<blockquote>
<p>垃圾回收器线程就是一种守护线程</p>
<p>Tomcat 中的 Acceptor 和 Poller 线程都是守护线程，所以 Tomcat 接收到 shutdown 命令后，不会等待它们处理完当前请求</p>
</blockquote>
<h3 id="线程的状态"><a href="#线程的状态" class="headerlink" title="线程的状态"></a>线程的状态</h3><p><strong>五种状态：</strong>从操作系统层面来描述</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220408144100948.png" alt="image-20220408144100948"></p>
<ul>
<li><p>初始状态仅是在语言层面创建了线程对象，还未与操作系统线程关联 </p>
</li>
<li><p>就绪状态指该线程已经被创建（与操作系统线程关联），可以由 CPU 调度执行</p>
</li>
<li><p>运行状态指获取了 CPU 时间片运行中的状态</p>
<ul>
<li>当 CPU 时间片用完，会从【运行状态】转换至【可运行状态】，会导致线程的上下文切换 </li>
</ul>
</li>
<li><p> 阻塞状态：如果调用了阻塞 API，如 BIO 读写文件，这时该线程实际不会用到 CPU，会导致线程上下文切换，进入 【阻塞状态】；等 BIO 操作完毕，会由操作系统唤醒阻塞的线程，转换至【可运行状态】；与【可运行状态】的区别是，对【阻塞状态】的线程来说只要它们一直不唤醒，调度器就一直不会考虑调度它们</p>
</li>
<li><p> 终止状态：表示线程已经执行完毕，生命周期已经结束，不会再转换为其它状态</p>
</li>
</ul>
<p><strong>六种状态：</strong>从 Java API 层面来描述的，根据 Thread.State 枚举，分为六种状态</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220408144703862.png" alt="image-20220408144703862"></p>
<ul>
<li>NEW 线程刚被创建，但是还没有调用 start() 方法，相当于初始状态。</li>
<li>RUNNABLE 当调用了 start() 方法之后，注意，Java API 层面的 RUNNABLE 状态涵盖了 操作系统 层面的 【可运行状态】、【运行状态】和【阻塞状态】（由于 BIO 导致的线程阻塞，在 Java 里无法区分，仍然认为是可运行）</li>
<li>BLOCKED ，WAITING ，TIMED_WAITING 都是 Java API 层面对【阻塞状态】的细分；线程未获取到锁，就是BLOCKED ，sleep有具体时长就是TIMED_WAITING，没有时长就是WAITING 。</li>
<li>TERMINATED 当线程代码运行结束</li>
</ul>
<h4 id="线程状态的转换"><a href="#线程状态的转换" class="headerlink" title="线程状态的转换"></a>线程状态的转换</h4><p><strong>情况 1 NEW –&gt; RUNNABLE</strong> </p>
<ul>
<li>调用 t.start() 方法时，由 NEW –&gt; RUNNABLE</li>
</ul>
<p><strong>情况 2 RUNNABLE &lt;–&gt; WAITING</strong></p>
<p>t 线程用 synchronized(obj) 获取了对象锁后</p>
<ul>
<li>调用 obj.wait() 方法时，t 线程从 RUNNABLE –&gt; WAITING</li>
<li>调用 obj.notify() ， obj.notifyAll() ， t.interrupt() 时<ul>
<li>竞争锁成功，t 线程从 WAITING –&gt; RUNNABLE</li>
<li>竞争锁失败，t 线程从 WAITING –&gt; BLOCKED</li>
</ul>
</li>
</ul>
<p><strong>情况 3 RUNNABLE &lt;–&gt; WAITING</strong></p>
<ul>
<li><p>当前线程调用 t.join() 方法时，当前线程从 RUNNABLE –&gt; WAITING 注意是当前线程在t 线程对象的监视器上等待</p>
</li>
<li><p> t 线程运行结束，或调用了当前线程的 interrupt() 时，当前线程从 WAITING –&gt; RUNNABLE</p>
</li>
</ul>
<p><strong>情况 4 RUNNABLE &lt;–&gt; WAITING</strong></p>
<ul>
<li><p>当前线程调用 LockSupport.park() 方法会让当前线程从 RUNNABLE –&gt; WAITING </p>
</li>
<li><p>调用 LockSupport.unpark(目标线程) 或调用了线程 的 interrupt() ，会让目标线程从 WAITING –&gt;  RUNNABLE</p>
</li>
</ul>
<p><strong>情况 5 RUNNABLE &lt;–&gt; TIMED_WAITING</strong></p>
<p>t 线程用 synchronized(obj) 获取了对象锁后</p>
<ul>
<li>调用 obj.wait(long n) 方法时，t 线程从 RUNNABLE –&gt; TIMED_WAITING </li>
<li>t 线程等待时间超过了 n 毫秒，或调用 obj.notify() ， obj.notifyAll() ， t.interrupt() 时 <ul>
<li>竞争锁成功，t 线程从 TIMED_WAITING –&gt; RUNNABLE</li>
<li>竞争锁失败，t 线程从 TIMED_WAITING –&gt; BLOCKED</li>
</ul>
</li>
</ul>
<p><strong>情况 6 RUNNABLE &lt;–&gt; TIMED_WAITING</strong></p>
<ul>
<li><p>当前线程调用 t.join(long n) 方法时，当前线程从 RUNNABLE –&gt; TIMED_WAITING 注意是当前线程在t 线程对象的监视器上等待</p>
</li>
<li><p>当前线程等待时间超过了 n 毫秒，或t 线程运行结束，或调用了当前线程的 interrupt() 时，当前线程从 TIMED_WAITING –&gt; RUNNABLE</p>
</li>
</ul>
<p><strong>情况 7 RUNNABLE &lt;–&gt; TIMED_WAITING</strong> </p>
<ul>
<li><p>当前线程调用 Thread.sleep(long n) ，当前线程从 RUNNABLE –&gt; TIMED_WAITING </p>
</li>
<li><p>当前线程等待时间超过了 n 毫秒，当前线程从 TIMED_WAITING –&gt; RUNNABLE</p>
</li>
</ul>
<p><strong>情况 8 RUNNABLE &lt;–&gt; TIMED_WAITING</strong></p>
<ul>
<li><p>当前线程调用 LockSupport.parkNanos(long nanos) 或 LockSupport.parkUntil(long millis) 时，当前线 程从 RUNNABLE –&gt; TIMED_WAITING </p>
</li>
<li><p>调用 LockSupport.unpark(目标线程) 或调用了线程 的 interrupt() ，或是等待超时，会让目标线程从 TIMED_WAITING–&gt; RUNNABLE </p>
</li>
</ul>
<p><strong>情况 9 RUNNABLE &lt;–&gt; BLOCKED</strong></p>
<ul>
<li><p>t 线程用 synchronized(obj) 获取了对象锁时如果竞争失败，从 RUNNABLE –&gt; BLOCKED </p>
</li>
<li><p>持 obj 锁线程的同步代码块执行完毕，会唤醒该对象上所有 BLOCKED 的线程重新竞争，如果其中 t 线程竞争 成功，从 BLOCKED –&gt; RUNNABLE ，其它失败的线程仍然 BLOCKED </p>
</li>
</ul>
<p><strong>情况 10 RUNNABLE &lt;–&gt; TERMINATED</strong></p>
<p>当前线程所有代码运行完毕，进入 TERMINATED</p>
<h3 id="合理运用线程"><a href="#合理运用线程" class="headerlink" title="合理运用线程"></a>合理运用线程</h3><p>比如洗水壶1分钟，烧开水15分钟，洗茶壶1分钟，洗茶杯1分钟，拿茶叶1分钟，最后泡茶</p>
<p>任务可以划分为：由两个线程工作。</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220408153809828.png" alt="image-20220408153809828"></p>
<h2 id="共享模型之管程"><a href="#共享模型之管程" class="headerlink" title="共享模型之管程"></a>共享模型之管程</h2><p>并发共享变量带来的问题：原子性、可见性、有序性，见JMM</p>
<h3 id="变量的线程安全分析"><a href="#变量的线程安全分析" class="headerlink" title="变量的线程安全分析"></a>变量的线程安全分析</h3><p>成员变量和静态变量：</p>
<ul>
<li>如果它们没有共享，则线程安全</li>
<li>如果它们被共享了，根据它们的状态是否能够改变，又分两种情况 <ul>
<li>如果只有读操作，则线程安全 </li>
<li>如果有读写操作，则这段代码是临界区，需要考虑线程安全</li>
</ul>
</li>
</ul>
<p>局部变量：</p>
<ul>
<li>局部变量是线程安全的，局部变量会在每个线程的栈帧内存中被创建多份，因此不存在共享</li>
<li>但局部变量引用的对象则未必<ul>
<li>如果该对象没有逃离方法的作用访问，它是线程安全的 </li>
<li>如果该对象逃离方法的作用范围，需要考虑线程安全</li>
</ul>
</li>
</ul>
<p>比如：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">ThreadSafe</span> &#123;</span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title function_">method1</span><span class="params">(<span class="type">int</span> loopNumber)</span> &#123;</span><br><span class="line">		ArrayList&lt;String&gt; list = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;&gt;();</span><br><span class="line">    	<span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; loopNumber; i++) &#123;</span><br><span class="line">        	method2(list);</span><br><span class="line">        	method3(list);</span><br><span class="line">    	&#125;</span><br><span class="line">	&#125;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">method2</span><span class="params">(ArrayList&lt;String&gt; list)</span> &#123;</span><br><span class="line">        list.add(<span class="string">&quot;1&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">method3</span><span class="params">(ArrayList&lt;String&gt; list)</span> &#123;</span><br><span class="line">        list.remove(<span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">ThreadSafeSubClass</span> <span class="keyword">extends</span> <span class="title class_">ThreadSafe</span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">method3</span><span class="params">(ArrayList&lt;String&gt; list)</span> &#123;</span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">            list.remove(<span class="number">0</span>);</span><br><span class="line">        &#125;).start();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>上面代码种子类在方法中重开了一个线程，就会造成变量的共享</p>
<blockquote>
<p>从这个例子可以看出 private 或 final 提供【安全】的意义所在，体会开闭原则中的【闭】</p>
</blockquote>
<h3 id="常见线程安全类"><a href="#常见线程安全类" class="headerlink" title="常见线程安全类"></a>常见线程安全类</h3><ul>
<li>String</li>
<li>Integer等包装类</li>
<li>StringBuffer</li>
<li>Random</li>
<li>Vector</li>
<li>Hashtable</li>
<li>java.util.concurrent 包下的类</li>
</ul>
<p>这里说它们是线程安全的是指，多个线程调用它们同一个实例的某个方法时，是线程安全的</p>
<p>但注意它们<strong>多个方法的组合不是原子的</strong>，所以可能会出现线程安全问题</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Hashtable</span> <span class="variable">table</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Hashtable</span>();</span><br><span class="line"><span class="comment">// 线程1，线程2同时判断非空，导致线程的结果相互覆盖</span></span><br><span class="line"><span class="keyword">if</span>( table.get(<span class="string">&quot;key&quot;</span>) == <span class="literal">null</span>) &#123;</span><br><span class="line">	table.put(<span class="string">&quot;key&quot;</span>, value);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>String、Integer 等都是<strong>不可变类</strong>，因为其内部的状态不可以改变，因此它们的方法都是线程安全的</p>
<p><strong>实例分析</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//例1</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyServlet</span> <span class="keyword">extends</span> <span class="title class_">HttpServlet</span> &#123;</span><br><span class="line">    <span class="comment">// 不安全</span></span><br><span class="line">    Map&lt;String,Object&gt; map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line">    <span class="comment">// 安全</span></span><br><span class="line">    <span class="type">String</span> <span class="variable">S1</span> <span class="operator">=</span> <span class="string">&quot;...&quot;</span>;</span><br><span class="line">    <span class="comment">// 安全</span></span><br><span class="line">    <span class="keyword">final</span> <span class="type">String</span> <span class="variable">S2</span> <span class="operator">=</span> <span class="string">&quot;...&quot;</span>;</span><br><span class="line">    <span class="comment">// 不安全</span></span><br><span class="line">    <span class="type">Date</span> <span class="variable">D1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Date</span>();</span><br><span class="line">    <span class="comment">// 不安全，D2不能修改但是Date()的成员可能会变化</span></span><br><span class="line">    <span class="keyword">final</span> <span class="type">Date</span> <span class="variable">D2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Date</span>(); </span><br><span class="line"> 	<span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">doGet</span><span class="params">(HttpServletRequest request, HttpServletResponse response)</span> &#123;</span><br><span class="line">        <span class="comment">//使用上述变量</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//例2</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyServlet</span> <span class="keyword">extends</span> <span class="title class_">HttpServlet</span> &#123;</span><br><span class="line">	<span class="comment">// 不安全</span></span><br><span class="line">	<span class="keyword">private</span> <span class="type">UserService</span> <span class="variable">userService</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">UserServiceImpl</span>();</span><br><span class="line"> </span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">doGet</span><span class="params">(HttpServletRequest request, HttpServletResponse response)</span> &#123;</span><br><span class="line">		userService.update(...);</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UserServiceImpl</span> <span class="keyword">implements</span> <span class="title class_">UserService</span> &#123;</span><br><span class="line">	<span class="comment">// 记录调用次数</span></span><br><span class="line">	<span class="keyword">private</span> <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">	<span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">update</span><span class="params">()</span> &#123;</span><br><span class="line">		<span class="comment">//临界区，就会有线程安全问题</span></span><br><span class="line"> 		count++;</span><br><span class="line">	&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>案例：转账问题</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Account</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> money;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Account</span><span class="params">(<span class="type">int</span> money)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.money = money;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getMoney</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> money;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setMoney</span><span class="params">(<span class="type">int</span> money)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.money = money;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">transfer</span><span class="params">(Account target, <span class="type">int</span> amount)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">this</span>.money &gt; amount) &#123;</span><br><span class="line">            <span class="built_in">this</span>.setMoney(<span class="built_in">this</span>.getMoney() - amount);</span><br><span class="line">            target.setMoney(target.getMoney() + amount);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>加锁：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title function_">transfer</span><span class="params">(Account target, <span class="type">int</span> amount)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (<span class="built_in">this</span>.money &gt; amount) &#123;</span><br><span class="line">        <span class="built_in">this</span>.setMoney(<span class="built_in">this</span>.getMoney() - amount);</span><br><span class="line">        target.setMoney(target.getMoney() + amount);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>上面的加锁存在问题，synchronized在方法上相当于synchronized(this)，当不同的account对象调用transfer方法时无法起到加锁的效果。可考虑synchronized(Account.class)</p>
<h3 id="原理之synchronized"><a href="#原理之synchronized" class="headerlink" title="原理之synchronized"></a>原理之synchronized</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="type">Object</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line"><span class="keyword">static</span> <span class="type">int</span> <span class="variable">counter</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="keyword">synchronized</span> (lock) &#123;</span><br><span class="line">        counter++;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>对应的字节码为</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line">Code:</span><br><span class="line"> stack=2, locals=3, args_size=1</span><br><span class="line"> 0: getstatic #2 // &lt;- lock引用 （synchronized开始）</span><br><span class="line"> 3: dup</span><br><span class="line"> 4: astore_1 // lock引用 -&gt; slot 1</span><br><span class="line"> 5: monitorenter // 将 lock对象 MarkWord 置为 Monitor 指针</span><br><span class="line"> 6: getstatic #3 // &lt;- i</span><br><span class="line"> 9: iconst_1 // 准备常数 1</span><br><span class="line"> 10: iadd // +1</span><br><span class="line"> 11: putstatic #3 // -&gt; i</span><br><span class="line"> 14: aload_1 // &lt;- lock引用</span><br><span class="line"> 15: monitorexit // 将 lock对象 MarkWord 重置, 唤醒 EntryList</span><br><span class="line"> 16: goto 24</span><br><span class="line"> 19: astore_2 // e -&gt; slot 2 </span><br><span class="line"> 20: aload_1 // &lt;- lock引用</span><br><span class="line"> 21: monitorexit // 将 lock对象 MarkWord 重置, 唤醒 EntryList</span><br><span class="line"> 22: aload_2 // &lt;- slot 2 (e)</span><br><span class="line"> 23: athrow // throw e</span><br><span class="line"> 24: return</span><br><span class="line"> Exception table:</span><br><span class="line"> from to target type</span><br><span class="line"> 6 16 19 any</span><br><span class="line"> 19 22 19 any</span><br><span class="line"> LineNumberTable:</span><br><span class="line"> line 8: 0</span><br><span class="line"> line 9: 6</span><br><span class="line"> line 10: 14</span><br><span class="line"> line 11: 24</span><br><span class="line"> LocalVariableTable:</span><br><span class="line"> Start Length Slot Name Signature</span><br><span class="line"> 0 25 0 args [Ljava/lang/String;</span><br><span class="line"> StackMapTable: number_of_entries = 2</span><br><span class="line"> frame_type = 255 /* full_frame */</span><br><span class="line"> offset_delta = 19</span><br><span class="line"> locals = [ class &quot;[Ljava/lang/String;&quot;, class java/lang/Object ]</span><br><span class="line"> stack = [ class java/lang/Throwable ]</span><br><span class="line"> frame_type = 250 /* chop */</span><br><span class="line"> offset_delta = 4</span><br></pre></td></tr></table></figure>

<blockquote>
<p>方法级别的 synchronized 不会在字节码指令中有所体现</p>
<p>注意到有两个monitorexit指令，是为了同步代码块中出现异常时也能释放锁</p>
</blockquote>
<h4 id="Monitor"><a href="#Monitor" class="headerlink" title="Monitor"></a>Monitor</h4><p>Monitor 被翻译为监视器或管程</p>
<p>每个 Java 对象都可以关联一个 Monitor 对象，如果使用 synchronized 给对象上锁（重量级）之后，该对象头的 Mark Word 中就被设置指向 Monitor 对象的指针</p>
<p>Monitor 结构如下</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220414163820538.png" alt="image-20220414163820538"></p>
<ul>
<li>刚开始 Monitor 中 Owner 为 null</li>
<li>当 Thread-2 执行 synchronized(obj) 就会将 Monitor 的所有者 Owner 置为 Thread-2，Monitor中只能有一 个 Owner </li>
<li>在 Thread-2 上锁的过程中，如果 Thread-3，Thread-4，Thread-5 也来执行 synchronized(obj)，就会进入 EntryList BLOCKED</li>
<li>Thread-2 执行完同步代码块的内容，然后唤醒 EntryList 中等待的线程来竞争锁，竞争的时是非公平的</li>
<li>图中 WaitSet 中的 Thread-0，Thread-1 是之前获得过锁，但条件不满足进入 WAITING 状态的线程，见wait-notify 的分析</li>
</ul>
<h4 id="java对象头"><a href="#java对象头" class="headerlink" title="java对象头"></a>java对象头</h4><p>以 32 位虚拟机为例</p>
<p>普通对象</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220412144955927.png" alt="image-20220412144955927"></p>
<p>数组对象</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220412145014056.png" alt="image-20220412145014056"></p>
<p>其中 Mark Word 结构为：hashcode，新生代年龄，是否偏向锁，加锁状态</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220412145032711.png" alt="image-20220412145032711"></p>
<p>64 位虚拟机 Mark Word</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220412145103675.png" alt="image-20220412145103675"></p>
<h3 id="synchronized优化"><a href="#synchronized优化" class="headerlink" title="synchronized优化"></a>synchronized优化</h3><h4 id="轻量级锁"><a href="#轻量级锁" class="headerlink" title="轻量级锁"></a>轻量级锁</h4><p>如果一个对象虽然有多线程访问，但多线程访问的时间是错开的（也就是没有竞争），那么可以使用轻量级锁来优化。</p>
<p>假设有两个方法同步块，利用同一个对象加锁，具体流程为：</p>
<ul>
<li><p>创建锁记录（Lock Record）对象，<strong>每个线程都的栈帧都会包含一个锁记录的结构，内部可以存储锁定对象的Mark Word</strong></p>
</li>
<li><p>让锁记录中 Object reference 指向锁对象，并尝试用 cas 替换 Object 的 Mark Word，将 Mark Word 的值存入锁记录</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220412145249874.png" alt="image-20220412145249874"></p>
</li>
</ul>
<ul>
<li><p>如果 cas 替换成功，对象头中存储了锁记录地址和状态 00 ，表示由该线程给对象加锁，这时图示如下</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220412145300301.png" alt="image-20220412145300301"></p>
</li>
<li><p>如果 cas 失败，有两种情况</p>
<ul>
<li><p>如果是其它线程已经持有了该 Object 的轻量级锁，这时表明有竞争，进入锁膨胀过程</p>
</li>
<li><p>如果是自己执行了 synchronized 锁重入，那么再添加一条 Lock Record 作为重入的计数</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220412145310768.png" alt="image-20220412145310768"></p>
</li>
</ul>
</li>
</ul>
<ul>
<li>当退出 synchronized 代码块（解锁时）如果有取值为 null 的锁记录，表示有重入，这时重置锁记录，表示重入计数减一</li>
<li>当退出 synchronized 代码块（解锁时）锁记录的值不为 null，这时使用 cas 将 Mark Word 的值恢复给对象头<ul>
<li>成功，则解锁成功</li>
<li>失败，说明轻量级锁进行了锁膨胀或已经升级为重量级锁，进入重量级锁解锁流程</li>
</ul>
</li>
</ul>
<h4 id="锁膨胀"><a href="#锁膨胀" class="headerlink" title="锁膨胀"></a>锁膨胀</h4><p>如果在尝试加轻量级锁的过程中，CAS 操作无法成功，这时一种情况就是有其它线程为此对象加上了轻量级锁（有竞争），这时需要进行锁膨胀，将轻量级锁变为重量级锁。</p>
<p>具体流程为：</p>
<ul>
<li><p>当 Thread-1 进行轻量级加锁时，Thread-0 已经对该对象加了轻量级锁</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220412145520879.png" alt="image-20220412145520879"></p>
</li>
<li><p>这时 Thread-1 加轻量级锁失败，进入锁膨胀流程</p>
<ul>
<li>即为 Object 对象申请 Monitor 锁，让 Object 指向重量级锁地址</li>
<li>然后自己进入 Monitor 的 EntryList BLOCKED</li>
</ul>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220412145647660.png" alt="image-20220412145647660"></p>
</li>
<li><p>当 Thread-0 退出同步块解锁时，使用 cas 将 Mark Word 的值恢复给对象头，失败。这时会进入重量级解锁流程，即按照 Monitor 地址找到 Monitor 对象，设置 Owner 为 null，唤醒 EntryList 中 BLOCKED 线程</p>
</li>
</ul>
<h4 id="重量锁自旋优化"><a href="#重量锁自旋优化" class="headerlink" title="重量锁自旋优化"></a>重量锁自旋优化</h4><p>重量级锁竞争的时候，还可以使用自旋来进行优化，如果当前线程自旋成功（即这时候持锁线程已经退出了同步块，释放了锁），这时当前线程就可以避免阻塞。</p>
<p>在 Java 6 之后自旋锁是自适应的，比如对象刚刚的一次自旋操作成功过，那么认为这次自旋成功的可能性会高，就多自旋几次；反之，就少自旋甚至不自旋，总之，比较智能。</p>
<ul>
<li>自旋会占用 CPU 时间，单核 CPU 自旋就是浪费，多核 CPU 自旋才能发挥优势。</li>
<li>Java 7 之后不能控制是否开启自旋功能</li>
</ul>
<p>自旋重试成功的情况</p>
<table>
<thead>
<tr>
<th>线程 1 （cpu 1 上）</th>
<th>对象 Mark</th>
<th>线程 2 （cpu 2 上）</th>
</tr>
</thead>
<tbody><tr>
<td>访问同步块，获取 monitor</td>
<td>10（重量锁）重量锁指针</td>
<td></td>
</tr>
<tr>
<td>成功（加锁）</td>
<td>10（重量锁）重量锁指针</td>
<td></td>
</tr>
<tr>
<td>执行同步块</td>
<td>10（重量锁）重量锁指针</td>
<td></td>
</tr>
<tr>
<td>执行同步块</td>
<td>10（重量锁）重量锁指针</td>
<td>访问同步块，获取 monitor</td>
</tr>
<tr>
<td>执行同步块</td>
<td>10（重量锁）重量锁指针</td>
<td>自旋重试</td>
</tr>
<tr>
<td>执行完毕</td>
<td>10（重量锁）重量锁指针</td>
<td>自旋重试</td>
</tr>
<tr>
<td>成功解锁</td>
<td>01（无锁）</td>
<td>自旋重试</td>
</tr>
<tr>
<td></td>
<td>10（重量锁）重量锁指针</td>
<td>成功加锁</td>
</tr>
</tbody></table>
<p>自旋重试失败，线程2也会阻塞，需要重新唤醒。</p>
<h4 id="偏向锁"><a href="#偏向锁" class="headerlink" title="偏向锁"></a>偏向锁</h4><p>轻量级锁在没有竞争时（就自己这个线程），每次重入仍然需要执行 CAS 操作。Java 6 中引入了偏向锁来做进一步优化：只有第一次使用 CAS 将线程 ID 设置到对象的 Mark Word 头，之后发现这个线程 ID是自己的就表示没有竞争，不用重新 CAS.</p>
<ul>
<li>撤销偏向需要将持锁线程升级为轻量级锁，这个过程中所有线程需要暂停（STW），重量级的操作</li>
<li>访问对象的 hashCode 也会撤销偏向锁。对象头里本来会存放hashCode ，加偏向锁会变成线程id。</li>
<li>如果对象虽然被多个线程访问，但没有竞争，这时偏向了线程 T1 的对象仍有机会重新偏向 T2，重偏向会重置对象的 Thread ID</li>
<li>撤销偏向和重偏向都是批量进行的，以类为单位</li>
<li>如果撤销偏向到达某个阈值，整个类的所有对象都会变为不可偏向的</li>
<li>可以主动使用 -XX:-UseBiasedLocking禁用偏向锁</li>
</ul>
<h4 id="其他优化"><a href="#其他优化" class="headerlink" title="其他优化"></a>其他优化</h4><h5 id="减少上锁时间"><a href="#减少上锁时间" class="headerlink" title="减少上锁时间"></a>减少上锁时间</h5><p>同步代码块中尽量短</p>
<h5 id="减少锁的粒度"><a href="#减少锁的粒度" class="headerlink" title="减少锁的粒度"></a>减少锁的粒度</h5><p>将一个锁拆分为多个锁提高并发度，例如：</p>
<ul>
<li>ConcurrentHashMap，只锁一个链表</li>
<li>LongAdder 分为 base 和 cells 两部分。没有并发争用的时候或者是 cells 数组正在初始化的时候，会使用 CAS 来累加值到 base，有并发争用，会初始化 cells 数组，数组有多少个 cell，就允许有多少线程并行修改，最后将数组中每个 cell 累加，再加上 base 就是最终的值</li>
<li>LinkedBlockingQueue 入队和出队使用不同的锁，相对于LinkedBlockingArray只有一个锁效率要高</li>
</ul>
<h5 id="锁粗化"><a href="#锁粗化" class="headerlink" title="锁粗化"></a>锁粗化</h5><p>多次循环进入同步块不如同步块内多次循环</p>
<p>另外JVM可能会做如下优化，把多次append 的加锁操作粗化为一次（因为都是对同一个对象加锁，没必要重入多次）</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">new</span> <span class="title class_">StringBuffer</span>().append(<span class="string">&quot;a&quot;</span>).append(<span class="string">&quot;b&quot;</span>).append(<span class="string">&quot;c&quot;</span>);</span><br></pre></td></tr></table></figure>

<h5 id="锁消除"><a href="#锁消除" class="headerlink" title="锁消除"></a>锁消除</h5><p>JVM 会进行代码的逃逸分析，例如某个加锁对象是方法内局部变量，不会被其它线程所访问到，这时候就会被即时编译器忽略掉所有同步操作。</p>
<h5 id="读写分离"><a href="#读写分离" class="headerlink" title="读写分离"></a>读写分离</h5><ul>
<li><p>CopyOnWriteArrayList：即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候，不直接往当前容器添加，而是先将当前容器进行Copy，复制出一个新的容器，然后新的容器里添加元素，添加完元素之后，再将原容器的引用指向新的容器</p>
</li>
<li><p>ConyOnWriteSet</p>
</li>
</ul>
<h3 id="wait-notify"><a href="#wait-notify" class="headerlink" title="wait notify"></a>wait notify</h3><h4 id="原理之wait-notify"><a href="#原理之wait-notify" class="headerlink" title="原理之wait notify"></a>原理之wait notify</h4><p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220412152319522.png" alt="image-20220412152319522"></p>
<ul>
<li>Owner 线程发现条件不满足，调用 wait 方法，即可进入 WaitSet 变为 WAITING 状态</li>
<li>BLOCKED 和 WAITING 的线程都处于阻塞状态，不占用 CPU 时间片</li>
<li>BLOCKED 线程会在 Owner 线程释放锁时唤醒</li>
<li>WAITING 线程会在 Owner 线程调用 notify 或 notifyAll 时唤醒，但唤醒后并不意味者立刻获得锁，仍需进入 EntryList 重新竞争</li>
</ul>
<h4 id="API"><a href="#API" class="headerlink" title="API"></a>API</h4><ul>
<li>obj.wait() 让进入 object 监视器的线程到 waitSet 等待</li>
<li>obj.wait(long n) 有时限的等待, 到 n 毫秒后结束等待，或是被 notify</li>
<li>obj.notify() 在 object 上正在 waitSet 等待的线程中挑一个唤醒</li>
<li>obj.notifyAll() 让 object 上正在 waitSet 等待的线程全部唤醒</li>
</ul>
<p>它们都是线程之间进行协作的手段，<strong>都属于 Object 对象的方法，必须获得此对象的锁，才能调用这几个方法</strong>，也就是必须在同步代码块里面调用。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (obj) &#123;</span><br><span class="line">            log.debug(<span class="string">&quot;执行....&quot;</span>);</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                obj.wait(); <span class="comment">// 让线程在obj上一直等待下去</span></span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">            log.debug(<span class="string">&quot;其它代码....&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;).start();</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (obj) &#123;</span><br><span class="line">            log.debug(<span class="string">&quot;执行....&quot;</span>);</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                obj.wait(); <span class="comment">// 让线程在obj上一直等待下去</span></span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">            log.debug(<span class="string">&quot;其它代码....&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;).start();</span><br><span class="line">    <span class="comment">// 主线程两秒后执行</span></span><br><span class="line">    sleep(<span class="number">2</span>);</span><br><span class="line">    log.debug(<span class="string">&quot;唤醒 obj 上其它线程&quot;</span>);</span><br><span class="line">    <span class="keyword">synchronized</span> (obj) &#123;</span><br><span class="line">        obj.notify(); <span class="comment">// 唤醒obj上一个线程</span></span><br><span class="line">        <span class="comment">// obj.notifyAll(); // 唤醒obj上所有等待线程</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="sleep和wait"><a href="#sleep和wait" class="headerlink" title="sleep和wait"></a>sleep和wait</h4><ol>
<li>sleep 是 Thread 方法，而 wait 是 Object 的方法 </li>
<li>sleep 不需要强制和 synchronized 配合使用，但 wait 需要<br>和 synchronized 一起用 3</li>
<li>sleep 在睡眠的同时，<strong>不会释放对象锁的</strong>，但 wait 在等待的时候会释放对象锁</li>
<li>它们状态 TIMED_WAITING</li>
</ol>
<h4 id="用法"><a href="#用法" class="headerlink" title="用法"></a>用法</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">synchronized</span>(lock) &#123;</span><br><span class="line">    <span class="keyword">while</span>(条件不成立) &#123;</span><br><span class="line">        lock.wait();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 干活</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//另一个线程</span></span><br><span class="line"><span class="keyword">synchronized</span>(lock) &#123;</span><br><span class="line">    lock.notifyAll();<span class="comment">//需要唤醒所有的线程</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="park和unpark"><a href="#park和unpark" class="headerlink" title="park和unpark"></a>park和unpark</h3><p>它们是 LockSupport 类中的方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 暂停当前线程</span></span><br><span class="line">LockSupport.park(); </span><br><span class="line"><span class="comment">// 恢复某个线程的运行</span></span><br><span class="line">LockSupport.unpark(暂停线程对象)</span><br></pre></td></tr></table></figure>

<p>先 park 再 unpark</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Thread</span> <span class="variable">t1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">    log.debug(<span class="string">&quot;start...&quot;</span>);</span><br><span class="line">    sleep(<span class="number">1</span>);</span><br><span class="line">    log.debug(<span class="string">&quot;park...&quot;</span>);</span><br><span class="line">    LockSupport.park();</span><br><span class="line">    log.debug(<span class="string">&quot;resume...&quot;</span>);</span><br><span class="line">&#125;,<span class="string">&quot;t1&quot;</span>);</span><br><span class="line">t1.start();</span><br><span class="line">sleep(<span class="number">2</span>);</span><br><span class="line">log.debug(<span class="string">&quot;unpark...&quot;</span>);</span><br><span class="line">LockSupport.unpark(t1);</span><br></pre></td></tr></table></figure>

<p>先 unpark 再 park，子线程并不会停止。（unpark可以在park前后调用）</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Thread</span> <span class="variable">t1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">    log.debug(<span class="string">&quot;start...&quot;</span>);</span><br><span class="line">    sleep(<span class="number">2</span>);</span><br><span class="line">    log.debug(<span class="string">&quot;park...&quot;</span>);</span><br><span class="line">    LockSupport.park();</span><br><span class="line">    log.debug(<span class="string">&quot;resume...&quot;</span>);</span><br><span class="line">&#125;, <span class="string">&quot;t1&quot;</span>);</span><br><span class="line">t1.start();</span><br><span class="line">sleep(<span class="number">1</span>);</span><br><span class="line">log.debug(<span class="string">&quot;unpark...&quot;</span>);</span><br><span class="line">LockSupport.unpark(t1);</span><br></pre></td></tr></table></figure>

<p><strong>特点</strong></p>
<p>与 Object 的 wait &amp; notify 相比</p>
<ul>
<li>wait，notify 和 notifyAll 必须配合 Object Monitor 一起使用，而 park，unpark 不必</li>
<li>park &amp; unpark 是以线程为单位来【阻塞】和【唤醒】线程，而 notify 只能随机唤醒一个等待线程，notifyAll  是唤醒所有等待线程，就不那么【精确】</li>
<li>park &amp; unpark 可以先 unpark，而 wait &amp; notify 不能先 notify</li>
</ul>
<h5 id="原理之park和unpark"><a href="#原理之park和unpark" class="headerlink" title="原理之park和unpark"></a><strong>原理之</strong>park和unpark</h5><p>每个线程都有自己的一个 Parker 对象，由三部分组成 _counter ， _cond 和 _mutex </p>
<p>打个比喻</p>
<ul>
<li><p>线程就像一个旅人，Parker 就像他随身携带的背包，条件变量就好比背包中的帐篷。_counter 就好比背包中的备用干粮（0 为耗尽，1 为充足）</p>
</li>
<li><p>调用 park 就是要看需不需要停下来歇息</p>
<ul>
<li><p>如果备用干粮耗尽，那么钻进帐篷歇息</p>
</li>
<li><p>如果备用干粮充足，那么不需停留，继续前进</p>
</li>
</ul>
</li>
</ul>
<ul>
<li>调用 unpark，就好比令干粮充足<ul>
<li>如果这时线程还在帐篷，就唤醒让他继续前进</li>
<li>如果这时线程还在运行，那么下次他调用 park 时，仅是消耗掉备用干粮，不需停留继续前进</li>
<li>因为背包空间有限，多次调用 unpark 仅会补充一份备用干粮</li>
</ul>
</li>
</ul>
<p><strong>先park再unpark的情况：</strong></p>
<ol>
<li>当前线程调用 Unsafe.park() 方法 </li>
<li>检查 _counter ，本情况为 0，这时，获得 _mutex 互斥锁 </li>
<li>线程进入 _cond 条件变量阻塞 </li>
<li>设置 _counter = 0</li>
<li>调用 Unsafe.unpark(Thread_0) 方法，设置 _counter 为 1 </li>
<li>唤醒 _cond 条件变量中的线程</li>
<li>线程恢复运行</li>
<li>设置 _counter 为 0</li>
</ol>
<p><strong>先unpark再park的情况：</strong></p>
<ol>
<li><p>调用 Unsafe.unpark() 方法，设置 _counter 为 1</p>
</li>
<li><p>当前线程调用 Unsafe.park() 方法</p>
</li>
<li><p>检查 _counter ，本情况为 1，这时线程无需阻塞，继续运行</p>
</li>
<li><p>设置 _counter 为 0</p>
</li>
</ol>
<h3 id="多把锁"><a href="#多把锁" class="headerlink" title="多把锁"></a>多把锁</h3><p>多把不相干的锁</p>
<p>一间大屋子有两个功能：睡觉、学习，互不相干。 现在小南要学习，小女要睡觉，但如果只用一间屋子（一个对象锁）的话，那么并发度很低</p>
<p>解决方法是准备多个房间（多个对象锁） </p>
<p>例如</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">BigRoom</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">Object</span> <span class="variable">studyRoom</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">Object</span> <span class="variable">bedRoom</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">sleep</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (bedRoom) &#123;<span class="comment">//区别于锁住this</span></span><br><span class="line">            log.debug(<span class="string">&quot;sleeping 2 小时&quot;</span>);</span><br><span class="line">            Sleeper.sleep(<span class="number">2</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">study</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (studyRoom) &#123;</span><br><span class="line">            log.debug(<span class="string">&quot;study 1 小时&quot;</span>);</span><br><span class="line">            Sleeper.sleep(<span class="number">1</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>将锁的粒度细分</p>
<ul>
<li>好处，是可以增强并发度</li>
<li>坏处，如果一个线程需要同时获得多把锁，就容易发生死锁</li>
</ul>
<h3 id="活跃性"><a href="#活跃性" class="headerlink" title="活跃性"></a>活跃性</h3><h4 id="死锁"><a href="#死锁" class="headerlink" title="死锁"></a>死锁</h4><p>一个线程需要同时获取多把锁，这时就容易发生死锁</p>
<p>t1 线程 获得A对象 锁，接下来想获取 B对象的锁； t2 线程获得B对象锁，接下来想获取 A对象</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Object</span> <span class="variable">A</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line"><span class="type">Object</span> <span class="variable">B</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line"><span class="type">Thread</span> <span class="variable">t1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">    <span class="keyword">synchronized</span> (A) &#123;</span><br><span class="line">        log.debug(<span class="string">&quot;lock A&quot;</span>);</span><br><span class="line">        sleep(<span class="number">1</span>);</span><br><span class="line">        <span class="keyword">synchronized</span> (B) &#123;</span><br><span class="line">            log.debug(<span class="string">&quot;lock B&quot;</span>);</span><br><span class="line">            log.debug(<span class="string">&quot;操作...&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;, <span class="string">&quot;t1&quot;</span>);</span><br><span class="line"><span class="type">Thread</span> <span class="variable">t2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">    <span class="keyword">synchronized</span> (B) &#123;</span><br><span class="line">        log.debug(<span class="string">&quot;lock B&quot;</span>);</span><br><span class="line">        sleep(<span class="number">0.5</span>);</span><br><span class="line">        <span class="keyword">synchronized</span> (A) &#123;</span><br><span class="line">            log.debug(<span class="string">&quot;lock A&quot;</span>);</span><br><span class="line">            log.debug(<span class="string">&quot;操作...&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;, <span class="string">&quot;t2&quot;</span>);</span><br><span class="line">t1.start();</span><br><span class="line">t2.start();</span><br></pre></td></tr></table></figure>

<p><strong>定位死锁</strong></p>
<p>检测死锁可以使用 jconsole工具，或者使用 jps定位进程 id，再用 jstack 定位死锁：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">Java stack information for the threads listed above:</span><br><span class="line">===================================================</span><br><span class="line">&quot;Thread-1&quot;:</span><br><span class="line"> at thread.TestDeadLock.lambda$main$1(TestDeadLock.java:28)</span><br><span class="line"> - waiting to lock &lt;0x000000076b5bf1c0&gt; (a java.lang.Object)</span><br><span class="line"> - locked &lt;0x000000076b5bf1d0&gt; (a java.lang.Object)</span><br><span class="line"> at thread.TestDeadLock$$Lambda$2/883049899.run(Unknown Source)</span><br><span class="line"> at java.lang.Thread.run(Thread.java:745)</span><br><span class="line">&quot;Thread-0&quot;:</span><br><span class="line"> at thread.TestDeadLock.lambda$main$0(TestDeadLock.java:15)</span><br><span class="line"> - waiting to lock &lt;0x000000076b5bf1d0&gt; (a java.lang.Object)</span><br><span class="line"> - locked &lt;0x000000076b5bf1c0&gt; (a java.lang.Object)</span><br><span class="line"> at thread.TestDeadLock$$Lambda$1/495053715.run(Unknown Source)</span><br><span class="line"> at java.lang.Thread.run(Thread.java:745)</span><br><span class="line">Found 1 deadlock.</span><br></pre></td></tr></table></figure>

<ul>
<li>避免死锁要注意加锁顺序</li>
<li>另外如果由于某个线程进入了死循环，导致其它线程一直等待，对于这种情况 linux 下可以通过 top 先定位到 CPU 占用高的 Java 进程，再利用 top -Hp 进程id 来定位是哪个线程，最后再用 jstack 排查</li>
</ul>
<h4 id="活锁"><a href="#活锁" class="headerlink" title="活锁"></a>活锁</h4><p>活锁出现在两个线程互相改变对方的结束条件，最后谁也无法结束，例如</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TestLiveLock</span> &#123;</span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">volatile</span> <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> <span class="number">10</span>;</span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">Object</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">            <span class="comment">// 期望减到 0 退出循环</span></span><br><span class="line">            <span class="keyword">while</span> (count &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                sleep(<span class="number">0.2</span>);</span><br><span class="line">                count--;</span><br><span class="line">                log.debug(<span class="string">&quot;count: &#123;&#125;&quot;</span>, count);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;, <span class="string">&quot;t1&quot;</span>).start();</span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">            <span class="comment">// 期望超过 20 退出循环</span></span><br><span class="line">            <span class="keyword">while</span> (count &lt; <span class="number">20</span>) &#123;</span><br><span class="line">                sleep(<span class="number">0.2</span>);</span><br><span class="line">                count++;</span><br><span class="line">                log.debug(<span class="string">&quot;count: &#123;&#125;&quot;</span>, count);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;, <span class="string">&quot;t2&quot;</span>).start();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="饥饿"><a href="#饥饿" class="headerlink" title="饥饿"></a>饥饿</h4><p>很多教程中把饥饿定义为，一个线程由于优先级太低，始终得不到 CPU 调度执行，也不能够结束，饥饿的情况不易演示</p>
<h3 id="ReentrantLock"><a href="#ReentrantLock" class="headerlink" title="ReentrantLock"></a>ReentrantLock</h3><p>相对于 synchronized 它具备如下特点</p>
<ul>
<li>可中断</li>
<li>可以设置超时时间</li>
<li>可以设置为公平锁</li>
<li>支持多个条件变量 </li>
</ul>
<p>与 synchronized 一样，都支持可重入</p>
<p>基本语法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 获取锁</span></span><br><span class="line">reentrantLock.lock();</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">    <span class="comment">// 临界区</span></span><br><span class="line">&#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">    <span class="comment">// 释放锁</span></span><br><span class="line">    reentrantLock.unlock();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="可重入"><a href="#可重入" class="headerlink" title="可重入"></a>可重入</h4><p>可重入是指同一个线程如果首次获得了这把锁，那么因为它是这把锁的拥有者，因此有权利再次获取这把锁</p>
<p>如果是不可重入锁，那么第二次获得锁时，自己也会被锁挡住</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="type">ReentrantLock</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReentrantLock</span>();</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    method1();</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">method1</span><span class="params">()</span> &#123;</span><br><span class="line">    lock.lock();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        log.debug(<span class="string">&quot;execute method1&quot;</span>);</span><br><span class="line">        method2();</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        lock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">method2</span><span class="params">()</span> &#123;</span><br><span class="line">    lock.lock();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        log.debug(<span class="string">&quot;execute method2&quot;</span>);</span><br><span class="line">        method3();</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        lock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//方法1方法2都会执行</span></span><br></pre></td></tr></table></figure>

<h4 id="可打断"><a href="#可打断" class="headerlink" title="可打断"></a>可打断</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">ReentrantLock</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReentrantLock</span>();</span><br><span class="line"><span class="type">Thread</span> <span class="variable">t1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">    log.debug(<span class="string">&quot;启动...&quot;</span>);</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        lock.lockInterruptibly();<span class="comment">//lock()方法是不可打断的</span></span><br><span class="line">    &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">        log.debug(<span class="string">&quot;没有获得锁，返回&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        log.debug(<span class="string">&quot;获得了锁&quot;</span>);</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        lock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;, <span class="string">&quot;t1&quot;</span>);</span><br><span class="line">lock.lock();</span><br><span class="line">log.debug(<span class="string">&quot;主线程获得了锁&quot;</span>);</span><br><span class="line">t1.start();</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">    sleep(<span class="number">1</span>);</span><br><span class="line">    t1.interrupt();<span class="comment">//t1线程获取不到锁，阻塞，可以打断掉，别再等锁了</span></span><br><span class="line">    log.debug(<span class="string">&quot;执行打断&quot;</span>);</span><br><span class="line">&#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">    lock.unlock();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>可打断可以防止线程无限制的阻塞等待下去</p>
<h4 id="锁超时"><a href="#锁超时" class="headerlink" title="锁超时"></a>锁超时</h4><p>可以用来防止死锁</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">ReentrantLock</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReentrantLock</span>();</span><br><span class="line"><span class="type">Thread</span> <span class="variable">t1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">    log.debug(<span class="string">&quot;启动...&quot;</span>);</span><br><span class="line">    <span class="keyword">if</span> (!lock.tryLock()) &#123;</span><br><span class="line">        log.debug(<span class="string">&quot;获取立刻失败，返回&quot;</span>);</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        log.debug(<span class="string">&quot;获得了锁&quot;</span>);</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        lock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;, <span class="string">&quot;t1&quot;</span>);</span><br><span class="line">lock.lock();</span><br><span class="line">log.debug(<span class="string">&quot;获得了锁&quot;</span>);</span><br><span class="line">t1.start();</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">    sleep(<span class="number">2</span>);</span><br><span class="line">&#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">    lock.unlock();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<ul>
<li>tryLock()获取锁失败立刻返回false</li>
<li>tryLock(1, TimeUnit.SECONDS)获取锁超时返回false</li>
</ul>
<h4 id="公平锁"><a href="#公平锁" class="headerlink" title="公平锁"></a>公平锁</h4><p>ReentrantLock 默认是不公平的</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//默认为false</span></span><br><span class="line"><span class="keyword">public</span> <span class="title function_">ReentrantLock</span><span class="params">(<span class="type">boolean</span> fair)</span> &#123;</span><br><span class="line">    sync = fair ? <span class="keyword">new</span> <span class="title class_">FairSync</span>() : <span class="keyword">new</span> <span class="title class_">NonfairSync</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>不公平</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">ReentrantLock</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReentrantLock</span>(<span class="literal">false</span>);</span><br><span class="line">lock.lock();</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">500</span>; i++) &#123;</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        lock.lock();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            System.out.println(Thread.currentThread().getName() + <span class="string">&quot; running...&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            lock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;, <span class="string">&quot;t&quot;</span> + i).start();</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 1s 之后去争抢锁</span></span><br><span class="line">Thread.sleep(<span class="number">1000</span>);</span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">    System.out.println(Thread.currentThread().getName() + <span class="string">&quot; start...&quot;</span>);</span><br><span class="line">    lock.lock();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        System.out.println(Thread.currentThread().getName() + <span class="string">&quot; running...&quot;</span>);</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        lock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;, <span class="string">&quot;强行插入&quot;</span>).start();</span><br><span class="line">lock.unlock();</span><br></pre></td></tr></table></figure>

<p>强行插入，有机会在中间输出，如果是公平锁，必然是最后输出</p>
<p>公平锁一般没有必要，会降低并发度</p>
<h4 id="条件变量"><a href="#条件变量" class="headerlink" title="条件变量"></a>条件变量</h4><p>synchronized 中也有条件变量，就是 waitSet，当条件不满足时进入 waitSet 等待 </p>
<p>ReentrantLock 的条件变量比 synchronized 强大之处在于，支持多个条件变量的，这就好比</p>
<ul>
<li>synchronized 是那些不满足条件的线程都在一间休息室等消息</li>
<li>而 ReentrantLock 支持多间休息室，唤醒时也是按休息室来唤醒</li>
</ul>
<p>使用要点：</p>
<ul>
<li>await 前需要获得锁</li>
<li>await 执行后，会释放锁，进入 conditionObject 等待</li>
<li>await 的线程被唤醒（或打断、或超时）重新竞争 lock 锁 </li>
<li>竞争 lock 锁成功后，从 await 后继续执行</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="type">ReentrantLock</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReentrantLock</span>();</span><br><span class="line"><span class="comment">//创建条件变量</span></span><br><span class="line"><span class="keyword">static</span> <span class="type">Condition</span> <span class="variable">waitCigaretteQueue</span> <span class="operator">=</span> lock.newCondition();</span><br><span class="line"><span class="keyword">static</span> <span class="type">Condition</span> <span class="variable">waitbreakfastQueue</span> <span class="operator">=</span> lock.newCondition();</span><br><span class="line"><span class="keyword">static</span> <span class="keyword">volatile</span> <span class="type">boolean</span> <span class="variable">hasCigrette</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line"><span class="keyword">static</span> <span class="keyword">volatile</span> <span class="type">boolean</span> <span class="variable">hasBreakfast</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            lock.lock();</span><br><span class="line">            <span class="keyword">while</span> (!hasCigrette) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    waitCigaretteQueue.await();<span class="comment">//进入特定的waitSet</span></span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            log.debug(<span class="string">&quot;等到了它的烟&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            lock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;).start();</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            lock.lock();</span><br><span class="line">            <span class="keyword">while</span> (!hasBreakfast) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    waitbreakfastQueue.await();</span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            log.debug(<span class="string">&quot;等到了它的早餐&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            lock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;).start();</span><br><span class="line">    sleep(<span class="number">1</span>);</span><br><span class="line">    sendBreakfast();</span><br><span class="line">    sleep(<span class="number">1</span>);</span><br><span class="line">    sendCigarette();</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">sendCigarette</span><span class="params">()</span> &#123;</span><br><span class="line">    lock.lock();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        log.debug(<span class="string">&quot;送烟来了&quot;</span>);</span><br><span class="line">        hasCigrette = <span class="literal">true</span>;</span><br><span class="line">        waitCigaretteQueue.signal();<span class="comment">//唤醒特定的waitSet</span></span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        lock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">sendBreakfast</span><span class="params">()</span> &#123;</span><br><span class="line">    lock.lock();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        log.debug(<span class="string">&quot;送早餐来了&quot;</span>);</span><br><span class="line">        hasBreakfast = <span class="literal">true</span>;</span><br><span class="line">        waitbreakfastQueue.signal();</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        lock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//输出</span></span><br><span class="line"><span class="number">18</span>:<span class="number">52</span>:<span class="number">27.680</span> [main] c.TestCondition - 送早餐来了</span><br><span class="line"><span class="number">18</span>:<span class="number">52</span>:<span class="number">27.682</span> [Thread-<span class="number">1</span>] c.TestCondition - 等到了它的早餐</span><br><span class="line"><span class="number">18</span>:<span class="number">52</span>:<span class="number">28.683</span> [main] c.TestCondition - 送烟来了</span><br><span class="line"><span class="number">18</span>:<span class="number">52</span>:<span class="number">28.683</span> [Thread-<span class="number">0</span>] c.TestCondition - 等到了它的烟</span><br></pre></td></tr></table></figure>

<h2 id="共享模型之无锁"><a href="#共享模型之无锁" class="headerlink" title="共享模型之无锁"></a>共享模型之无锁</h2><p>问题的提出:</p>
<p>常见的取款方法，余额减去取款金额，多线程调用时存在线程安全问题</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> Integer balance;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">withdraw</span><span class="params">(Integer amount)</span> &#123;</span><br><span class="line">    balance -= amount;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>解决思路—锁</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title function_">withdraw</span><span class="params">(Integer amount)</span> &#123;</span><br><span class="line">    balance -= amount;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>解决思路-无锁</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> AtomicInteger balance;</span><br><span class="line"><span class="keyword">public</span> Integer <span class="title function_">getBalance</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> balance.get();</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">withdraw</span><span class="params">(Integer amount)</span> &#123;</span><br><span class="line">    <span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">prev</span> <span class="operator">=</span> balance.get();</span><br><span class="line">        <span class="type">int</span> <span class="variable">next</span> <span class="operator">=</span> prev - amount;</span><br><span class="line">        <span class="keyword">if</span> (balance.compareAndSet(prev, next)) &#123;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="CAS-与-volatile"><a href="#CAS-与-volatile" class="headerlink" title="CAS 与 volatile"></a>CAS 与 volatile</h3><p>CAS 即 Compare and Swap ，它体现的一种乐观锁的思想，比如多个线程要对一个共享的整型变量执行 +1 操作：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 需要不断尝试</span></span><br><span class="line"><span class="keyword">while</span>(<span class="literal">true</span>) &#123;</span><br><span class="line">	<span class="type">int</span> 旧值 = 共享变量 ; <span class="comment">// 比如拿到了当前值 0</span></span><br><span class="line">	<span class="type">int</span> 结果 = 旧值 + <span class="number">1</span>; <span class="comment">// 在旧值 0 的基础上增加 1 ，正确结果是 1</span></span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">    这时候如果别的线程把共享变量改成了 5，本线程的正确结果 1 就作废了，这时候</span></span><br><span class="line"><span class="comment">    compareAndSwap 返回 false，重新尝试，直到：</span></span><br><span class="line"><span class="comment">    compareAndSwap 返回 true，表示我本线程做修改的同时，别的线程没有干扰</span></span><br><span class="line"><span class="comment">    */</span></span><br><span class="line">    <span class="keyword">if</span>( compareAndSwap ( 旧值, 结果 )) &#123;</span><br><span class="line">        <span class="comment">//成功，退出循环</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>其实 CAS 的底层是 lock cmpxchg 指令（X86 架构），在单核 CPU 和多核 CPU 下都能够保证【比较-交 换】的原子性。</p>
<p>在多核状态下，某个核执行到带 lock 的指令时，CPU 会让总线锁住，当这个核把此指令执行完毕，再开启总线。这个过程中不会被线程的调度机制所打断，保证了多个线程对内存操作的准确性，是原子的。</p>
</blockquote>
<p><strong>CAS 必须借助 volatile 才能读取到共享变量的最新值来实现【比较并交换】的效果</strong></p>
<p>结合 CAS 和 volatile 可以实现无锁并发，适用于竞争不激烈、多核 CPU 的场景下。</p>
<h4 id="优势"><a href="#优势" class="headerlink" title="优势"></a>优势</h4><p>为什么无锁的效率高？</p>
<ul>
<li><p>无锁情况下，即使重试失败，线程始终在高速运行，没有停歇，而 synchronized 会让线程在没有获得锁的时候，发生上下文切换，进入阻塞。频繁的上下文切换对性能影响较大。</p>
</li>
<li><p>但无锁情况下，因为线程要保持运行，需要额外 CPU 的支持。由于没有分到时间片，仍然会进入可运行状态，还是会导致上下文切换。多核CPU才能体现无锁的优势，且线程数不超多CPU核心数。</p>
</li>
<li><p>但如果竞争激烈，可以想到重试必然频繁发生，反而效率会受影响</p>
</li>
</ul>
<h4 id="乐观锁与悲观锁"><a href="#乐观锁与悲观锁" class="headerlink" title="乐观锁与悲观锁"></a>乐观锁与悲观锁</h4><ul>
<li><p>CAS 是基于乐观锁的思想：最乐观的估计，不怕别的线程来修改共享变量，就算改了也没关系，我吃亏点再 重试呗。</p>
</li>
<li><p>synchronized 是基于悲观锁的思想：最悲观的估计，得防着其它线程来修改共享变量，我上了锁你们都别想 改，我改完了解开锁，你们才有机会。</p>
</li>
</ul>
<h3 id="原子操作类"><a href="#原子操作类" class="headerlink" title="原子操作类"></a>原子操作类</h3><p>juc（java.util.concurrent）中提供了原子操作类，可以提供线程安全的操作，例如：AtomicInteger、 AtomicBoolean等，它们底层就是采用 CAS 技术 + volatile 来实现的。</p>
<h4 id="原子整数"><a href="#原子整数" class="headerlink" title="原子整数"></a>原子整数</h4><p>以 AtomicInteger 为例</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">AtomicInteger</span> <span class="variable">i</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AtomicInteger</span>(<span class="number">0</span>);</span><br><span class="line"><span class="comment">// i++</span></span><br><span class="line">System.out.println(i.getAndIncrement());</span><br><span class="line"><span class="comment">// ++i</span></span><br><span class="line">System.out.println(i.incrementAndGet());</span><br><span class="line"><span class="comment">// --i</span></span><br><span class="line">System.out.println(i.decrementAndGet());</span><br><span class="line"><span class="comment">// 类似于 i--</span></span><br><span class="line">System.out.println(i.getAndDecrement());</span><br><span class="line"><span class="comment">// 获取并加值</span></span><br><span class="line">System.out.println(i.getAndAdd(<span class="number">5</span>));</span><br><span class="line"><span class="comment">// 加值并获取</span></span><br><span class="line">System.out.println(i.addAndGet(-<span class="number">5</span>));</span><br><span class="line"><span class="comment">// 获取并更新</span></span><br><span class="line">System.out.println(i.getAndUpdate(p -&gt; p - <span class="number">2</span>));</span><br><span class="line"><span class="comment">// 更新并获取</span></span><br><span class="line">System.out.println(i.updateAndGet(p -&gt; p + <span class="number">2</span>));</span><br><span class="line"><span class="comment">// 获取并计算</span></span><br><span class="line">System.out.println(i.getAndAccumulate(<span class="number">10</span>, (p, x) -&gt; p + x));</span><br><span class="line"><span class="comment">// 计算并获取</span></span><br><span class="line">System.out.println(i.accumulateAndGet(-<span class="number">10</span>, (p, x) -&gt; p + x));</span><br></pre></td></tr></table></figure>

<p>源码</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="type">int</span> <span class="title function_">updateAndGet</span><span class="params">(IntUnaryOperator updateFunction)</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">prev</span> <span class="operator">=</span> get(), next = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">boolean</span> <span class="variable">haveNext</span> <span class="operator">=</span> <span class="literal">false</span>;;) &#123;</span><br><span class="line">        <span class="keyword">if</span> (!haveNext)</span><br><span class="line">            next = updateFunction.applyAsInt(prev);</span><br><span class="line">        <span class="keyword">if</span> (weakCompareAndSetVolatile(prev, next))</span><br><span class="line">            <span class="keyword">return</span> next;</span><br><span class="line">        haveNext = (prev == (prev = get()));</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="原子引用"><a href="#原子引用" class="headerlink" title="原子引用"></a>原子引用</h4><p>引用数据类型的共享变量</p>
<ul>
<li>AtomicReference</li>
<li>AtomicMarkableReference</li>
<li>AtomicStampedReference</li>
</ul>
<p>同样的之前的取款问题</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">AtomicReference&lt;BigDecimal&gt; ref;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">withdraw</span><span class="params">(BigDecimal amount)</span> &#123;</span><br><span class="line">    <span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">        <span class="type">BigDecimal</span> <span class="variable">prev</span> <span class="operator">=</span> ref.get();</span><br><span class="line">        <span class="type">BigDecimal</span> <span class="variable">next</span> <span class="operator">=</span> prev.subtract(amount);</span><br><span class="line">        <span class="keyword">if</span> (ref.compareAndSet(prev, next)) &#123;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="ABA-问题及解决"><a href="#ABA-问题及解决" class="headerlink" title="ABA 问题及解决"></a>ABA 问题及解决</h5><p>主线程仅能判断出共享变量的值与最初值 A 是否相同，不能感知到这种从 A 改为 B 又 改回 A 的情况，如果主线程希望：</p>
<p>只要有其它线程【动过了】共享变量，那么自己的 cas 就算失败，这时，仅比较值是不够的，需要再加一个版本号</p>
<p><strong>AtomicStampedReference</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> AtomicStampedReference&lt;String&gt; ref = <span class="keyword">new</span> <span class="title class_">AtomicStampedReference</span>&lt;&gt;(<span class="string">&quot;A&quot;</span>, <span class="number">0</span>);</span><br><span class="line"><span class="comment">// 获取值 A</span></span><br><span class="line"><span class="type">String</span> <span class="variable">prev</span> <span class="operator">=</span> ref.getReference();</span><br><span class="line"><span class="comment">// 获取版本号</span></span><br><span class="line"><span class="type">int</span> <span class="variable">stamp</span> <span class="operator">=</span> ref.getStamp();</span><br><span class="line"><span class="comment">// 尝试改为 C,版本号+1</span></span><br><span class="line">log.debug(<span class="string">&quot;change A-&gt;C &#123;&#125;&quot;</span>, ref.compareAndSet(prev, <span class="string">&quot;C&quot;</span>, stamp, stamp + <span class="number">1</span>));</span><br></pre></td></tr></table></figure>

<p>但是有时候，并不关心引用变量更改了几次，只是单纯的关心是否更改过，所以就有了 AtomicMarkableReference</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">AtomicMarkableReference&lt;GarbageBag&gt; ref = <span class="keyword">new</span> <span class="title class_">AtomicMarkableReference</span>&lt;&gt;(bag, <span class="literal">true</span>);</span><br><span class="line"><span class="type">boolean</span> <span class="variable">success</span> <span class="operator">=</span> ref.compareAndSet(prev, <span class="keyword">new</span> <span class="title class_">GarbageBag</span>(<span class="string">&quot;空垃圾袋&quot;</span>), <span class="literal">true</span>, <span class="literal">false</span>);</span><br></pre></td></tr></table></figure>

<h4 id="原子数组"><a href="#原子数组" class="headerlink" title="原子数组"></a>原子数组</h4><p>多个线程访问数组的线程安全问题</p>
<ul>
<li>AtomicIntegerArray</li>
<li>AtomicLongArray</li>
<li>AtomicReferenceArray</li>
</ul>
<h4 id="字段更新器"><a href="#字段更新器" class="headerlink" title="字段更新器"></a>字段更新器</h4><p>保护对象的字段</p>
<ul>
<li>AtomicReferenceFieldUpdater // 域 字段 </li>
<li>AtomicIntegerFieldUpdater</li>
<li>AtomicLongFieldUpdater</li>
</ul>
<p>利用字段更新器，可以针对对象的某个域（Field）进行原子操作，只能配合 volatile 修饰的字段使用，否则会出现异常</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Test</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">Student</span> <span class="variable">stu</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Student</span>();</span><br><span class="line">        <span class="type">AtomicReferenceFieldUpdater</span> <span class="variable">updater</span> <span class="operator">=</span> AtomicReferenceFieldUpdater.newUpdater(Student.class,String.class,<span class="string">&quot;name&quot;</span>);</span><br><span class="line">        updater.compareAndSet(stu,<span class="literal">null</span>,<span class="string">&quot;张三&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Student</span>&#123;</span><br><span class="line">    <span class="keyword">volatile</span> String name;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="原子累加器"><a href="#原子累加器" class="headerlink" title="原子累加器"></a>原子累加器</h4><p>比较 AtomicLong 与 LongAdder， LongAdder性能更高</p>
<p>性能提升的原因很简单，就是在有竞争时，设置多个累加单元，Therad-0 累加 Cell[0]，而 Thread-1 累加 Cell[1]… 最后将结果汇总。这样它们在累加时操作的不同的 Cell 变量，因此减少了 CAS 重试失败，从而提高性能。</p>
<p><strong>源码</strong></p>
<p>LongAdder是并发大师 @author Doug Lea （大哥李）的作品，设计的非常精巧</p>
<p>LongAdder类有几个关键域</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 累加单元数组, 懒惰初始化</span></span><br><span class="line"><span class="keyword">transient</span> <span class="keyword">volatile</span> Cell[] cells;</span><br><span class="line"><span class="comment">// 基础值, 如果没有竞争, 则用 cas 累加这个域</span></span><br><span class="line"><span class="keyword">transient</span> <span class="keyword">volatile</span> <span class="type">long</span> base;</span><br><span class="line"><span class="comment">// 在 cells 创建或扩容时, 置为 1, 表示加锁</span></span><br><span class="line"><span class="keyword">transient</span> <span class="keyword">volatile</span> <span class="type">int</span> cellsBusy;</span><br></pre></td></tr></table></figure>

<h5 id="cas-锁"><a href="#cas-锁" class="headerlink" title="cas 锁"></a>cas 锁</h5><p>cellsBusy是利用cas 加锁</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//实际不建议这么操作</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">LockCas</span> &#123;</span><br><span class="line">    <span class="comment">//0表示没加锁，1加锁</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">AtomicInteger</span> <span class="variable">state</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AtomicInteger</span>(<span class="number">0</span>);</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">lock</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (state.compareAndSet(<span class="number">0</span>, <span class="number">1</span>)) &#123;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">unlock</span><span class="params">()</span> &#123;</span><br><span class="line">        log.debug(<span class="string">&quot;unlock...&quot;</span>);</span><br><span class="line">        state.set(<span class="number">0</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="原理之伪共享"><a href="#原理之伪共享" class="headerlink" title="原理之伪共享"></a>原理之伪共享</h5><p>其中 Cell 即为累加单元</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 注解的作用防止缓存行伪共享</span></span><br><span class="line"><span class="meta">@sun</span>.misc.Contended</span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">Cell</span> &#123;</span><br><span class="line">    <span class="keyword">volatile</span> <span class="type">long</span> value;</span><br><span class="line">    Cell(<span class="type">long</span> x) &#123; value = x; &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 最重要的方法, 用来 cas 方式进行累加, prev 表示旧值, next 表示新值</span></span><br><span class="line">    <span class="keyword">final</span> <span class="type">boolean</span> <span class="title function_">cas</span><span class="params">(<span class="type">long</span> prev, <span class="type">long</span> next)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> UNSAFE.compareAndSwapLong(<span class="built_in">this</span>, valueOffset, prev, next);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 省略不重要代码</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>得从缓存说起</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220415170030594.png" alt="image-20220415170030594"></p>
<p>缓存与内存的速度比较</p>
<table>
<thead>
<tr>
<th>从 cpu 到</th>
<th>大约需要的时钟周期</th>
</tr>
</thead>
<tbody><tr>
<td>寄存器</td>
<td>1 cycle (4GHz 的 CPU 约为0.25ns</td>
</tr>
<tr>
<td>L1</td>
<td>3~4 cycle</td>
</tr>
<tr>
<td>L2</td>
<td>10~20 cycle</td>
</tr>
<tr>
<td>L3</td>
<td>40~45 cycle</td>
</tr>
<tr>
<td>内存</td>
<td>120~240 cycle</td>
</tr>
</tbody></table>
<p>因为 CPU 与 内存的速度差异很大，需要靠预读数据至缓存来提升效率。</p>
<p>而缓存以缓存行为单位，每个缓存行对应着一块内存，一般是 64 byte（8 个 long）</p>
<p>缓存的加入会造成数据副本的产生，即同一份数据会缓存在不同核心的缓存行中</p>
<p>CPU 要保证数据的一致性，如果某个 CPU 核心更改了数据，其它 CPU 核心对应的整个缓存行必须失效</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220415171107232.png" alt="image-20220415171107232"></p>
<p>因为 Cell 是数组形式，在内存中是连续存储的，一个 Cell 为 24 字节（16 字节的对象头和 8 字节的 value），因此缓存行可以存下 2 个的 Cell 对象。这样问题来了：</p>
<ul>
<li>Core-0 要修改 Cell[0]</li>
<li>Core-1 要修改 Cell[1]</li>
</ul>
<p>无论谁修改成功，都会导致对方 Core 的缓存行失效，比如 Core-0 中 Cell[0]=6000, Cell[1]=8000 要累加 Cell[0]=6001, Cell[1]=8000 ，这时会让 Core-1 的缓存行失效</p>
<p>@sun.misc.Contended 用来解决这个问题，它的原理是在使用此注解的对象或字段的前后各增加 128 字节大小的 padding，从而让 CPU 将对象预读至缓存时占用不同的缓存行，这样，不会造成对方缓存行的失效</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220415171602176.png" alt="image-20220415171602176"></p>
<p>累加主要调用下面的方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">add</span><span class="params">(<span class="type">long</span> x)</span> &#123;</span><br><span class="line">    <span class="comment">// as 为累加单元数组,b 为基础值,x 为累加值</span></span><br><span class="line">    Cell[] as; <span class="type">long</span> b, v; <span class="type">int</span> m; Cell a;</span><br><span class="line">    <span class="comment">// 进入 if 的两个条件</span></span><br><span class="line">    <span class="comment">// 1. as 有值, 表示已经发生过竞争, 进入 if</span></span><br><span class="line">    <span class="comment">// 2. cas 给 base 累加时失败了, 表示 base 发生了竞争, 进入 if</span></span><br><span class="line">    <span class="keyword">if</span> ((as = cells) != <span class="literal">null</span> || !casBase(b = base, b + x)) &#123;</span><br><span class="line">        <span class="comment">// uncontended 表示 cell 没有竞争</span></span><br><span class="line">        <span class="type">boolean</span> <span class="variable">uncontended</span> <span class="operator">=</span> <span class="literal">true</span>;</span><br><span class="line">        <span class="keyword">if</span> (</span><br><span class="line">            <span class="comment">// as 还没有创建</span></span><br><span class="line">            as == <span class="literal">null</span> || (m = as.length - <span class="number">1</span>) &lt; <span class="number">0</span> ||</span><br><span class="line">            <span class="comment">// 当前线程对应的 cell 还没有</span></span><br><span class="line">            (a = as[getProbe() &amp; m]) == <span class="literal">null</span> ||</span><br><span class="line">            <span class="comment">// cas 给当前线程的 cell 累加失败 uncontended=false ( a 为当前线程的 cell )</span></span><br><span class="line">            !(uncontended = a.cas(v = a.value, v + x))</span><br><span class="line">        ) &#123;</span><br><span class="line">            <span class="comment">// 进入 cell 数组创建、cell 创建的流程</span></span><br><span class="line">            longAccumulate(x, <span class="literal">null</span>, uncontended);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>add 流程图</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220416101640650.png" alt="image-20220416101640650"></p>
<p>longAccumulate源码</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">final</span> <span class="keyword">void</span> <span class="title function_">longAccumulate</span><span class="params">(<span class="type">long</span> x, LongBinaryOperator fn,</span></span><br><span class="line"><span class="params">                          <span class="type">boolean</span> wasUncontended)</span> &#123;</span><br><span class="line">    <span class="type">int</span> h;</span><br><span class="line">    <span class="comment">// 当前线程还没有对应的 cell, 需要随机生成一个 h 值用来将当前线程绑定到 cell</span></span><br><span class="line">    <span class="keyword">if</span> ((h = getProbe()) == <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="comment">// 初始化 probe</span></span><br><span class="line">        ThreadLocalRandom.current();</span><br><span class="line">        <span class="comment">// h 对应新的 probe 值, 用来对应 cell</span></span><br><span class="line">        h = getProbe();</span><br><span class="line">        wasUncontended = <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// collide 为 true 表示需要扩容</span></span><br><span class="line">    <span class="type">boolean</span> <span class="variable">collide</span> <span class="operator">=</span> <span class="literal">false</span>; </span><br><span class="line">    <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">        Cell[] as; Cell a; <span class="type">int</span> n; <span class="type">long</span> v;</span><br><span class="line">        <span class="comment">// 已经有了 cells</span></span><br><span class="line">        <span class="keyword">if</span> ((as = cells) != <span class="literal">null</span> &amp;&amp; (n = as.length) &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="comment">// 还没有 cell</span></span><br><span class="line">            <span class="keyword">if</span> ((a = as[(n - <span class="number">1</span>) &amp; h]) == <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="comment">// 为 cellsBusy 加锁, 创建 cell, cell 的初始累加值为 x</span></span><br><span class="line">                <span class="comment">// 成功则 break, 否则继续 continue 循环</span></span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// 有竞争, 改变线程对应的 cell 来重试 cas</span></span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (!wasUncontended)</span><br><span class="line">                wasUncontended = <span class="literal">true</span>;</span><br><span class="line">            <span class="comment">// cas 尝试累加, fn 配合 LongAccumulator 不为 null, 配合 LongAdder 为 null</span></span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (a.cas(v = a.value, ((fn == <span class="literal">null</span>) ? v + x : fn.applyAsLong(v, x))))</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            <span class="comment">// 如果 cells 长度已经超过了最大长度, 或者已经扩容, 改变线程对应的 cell 来重试 cas</span></span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (n &gt;= NCPU || cells != as)</span><br><span class="line">                collide = <span class="literal">false</span>;</span><br><span class="line">            <span class="comment">// 确保 collide 为 false 进入此分支, 就不会进入下面的 else if 进行扩容了</span></span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (!collide)</span><br><span class="line">                collide = <span class="literal">true</span>;</span><br><span class="line">            <span class="comment">// 加锁</span></span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (cellsBusy == <span class="number">0</span> &amp;&amp; casCellsBusy()) &#123;</span><br><span class="line">                <span class="comment">// 加锁成功, 扩容</span></span><br><span class="line">                <span class="keyword">continue</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// 改变线程对应的 cell</span></span><br><span class="line">            h = advanceProbe(h);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 还没有 cells, 尝试给 cellsBusy 加锁</span></span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (cellsBusy == <span class="number">0</span> &amp;&amp; cells == as &amp;&amp; casCellsBusy()) &#123;</span><br><span class="line">            <span class="comment">// 加锁成功, 初始化 cells, 最开始长度为 2, 并填充一个 cell</span></span><br><span class="line">            <span class="comment">// 成功则 break;</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 上两种情况失败, 尝试给 base 累加</span></span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (casBase(v = base, ((fn == <span class="literal">null</span>) ? v + x : fn.applyAsLong(v, x))))</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>longAccumulate 流程图</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220416101712436.png" alt="image-20220416101712436"></p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220416101748929.png" alt="image-20220416101748929"></p>
<p>每个线程刚进入 longAccumulate 时，会尝试对应一个 cell 对象（找到一个坑位）</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220416101826307.png" alt="image-20220416101826307"></p>
<p>获取最终结果通过 sum 方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="type">long</span> <span class="title function_">sum</span><span class="params">()</span> &#123;</span><br><span class="line">    Cell[] as = cells; Cell a;</span><br><span class="line">    <span class="type">long</span> <span class="variable">sum</span> <span class="operator">=</span> base;</span><br><span class="line">    <span class="keyword">if</span> (as != <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; as.length; ++i) &#123;</span><br><span class="line">            <span class="keyword">if</span> ((a = as[i]) != <span class="literal">null</span>)</span><br><span class="line">                sum += a.value;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> sum;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="Unsafe"><a href="#Unsafe" class="headerlink" title="Unsafe"></a>Unsafe</h3><p>Unsafe 对象提供了非常底层的，操作内存、线程的方法，Unsafe 对象不能直接调用，只能通过反射获得</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">UnsafeAccessor</span> &#123;</span><br><span class="line">    <span class="keyword">static</span> Unsafe unsafe;</span><br><span class="line">    <span class="keyword">static</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123; </span><br><span class="line">            <span class="type">Field</span> <span class="variable">theUnsafe</span> <span class="operator">=</span> Unsafe.class.getDeclaredField(<span class="string">&quot;theUnsafe&quot;</span>);</span><br><span class="line">            theUnsafe.setAccessible(<span class="literal">true</span>);</span><br><span class="line">            unsafe = (Unsafe) theUnsafe.get(<span class="literal">null</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (NoSuchFieldException | IllegalAccessException e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(e);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">static</span> Unsafe <span class="title function_">getUnsafe</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> unsafe;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>Unsafe CAS 操作</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@Data</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Student</span> &#123;</span><br><span class="line">    <span class="keyword">volatile</span> <span class="type">int</span> id;</span><br><span class="line">    <span class="keyword">volatile</span> String name;</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">Unsafe</span> <span class="variable">unsafe</span> <span class="operator">=</span> UnsafeAccessor.getUnsafe();</span><br><span class="line"><span class="type">Field</span> <span class="variable">id</span> <span class="operator">=</span> Student.class.getDeclaredField(<span class="string">&quot;id&quot;</span>);</span><br><span class="line"><span class="type">Field</span> <span class="variable">name</span> <span class="operator">=</span> Student.class.getDeclaredField(<span class="string">&quot;name&quot;</span>);</span><br><span class="line"><span class="comment">// 获得成员变量的偏移量</span></span><br><span class="line"><span class="type">long</span> <span class="variable">idOffset</span> <span class="operator">=</span> unsafe.objectFieldOffset(id);</span><br><span class="line"><span class="type">long</span> <span class="variable">nameOffset</span> <span class="operator">=</span> unsafe.objectFieldOffset(name);</span><br><span class="line"><span class="type">Student</span> <span class="variable">student</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Student</span>();</span><br><span class="line"><span class="comment">// 使用 cas 方法替换成员变量的值</span></span><br><span class="line">unsafe.compareAndSwapInt(student, idOffset, <span class="number">0</span>, <span class="number">20</span>); <span class="comment">// 返回 true</span></span><br><span class="line">unsafe.compareAndSwapObject(student, nameOffset, <span class="literal">null</span>, <span class="string">&quot;张三&quot;</span>); <span class="comment">// 返回 true</span></span><br><span class="line">System.out.println(student);</span><br></pre></td></tr></table></figure>

<p>使用自定义的 AtomicData 实现之前线程安全的原子整数 Account 实现</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">AtomicData</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">volatile</span> <span class="type">int</span> data;</span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> Unsafe unsafe;</span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">long</span> DATA_OFFSET;</span><br><span class="line">    <span class="keyword">static</span> &#123;</span><br><span class="line">        unsafe = UnsafeAccessor.getUnsafe();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// data 属性在 DataContainer 对象中的偏移量，用于 Unsafe 直接访问该属性</span></span><br><span class="line">            DATA_OFFSET = unsafe.objectFieldOffset(AtomicData.class.getDeclaredField(<span class="string">&quot;data&quot;</span>));</span><br><span class="line">        &#125; <span class="keyword">catch</span> (NoSuchFieldException e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(e);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">AtomicData</span><span class="params">(<span class="type">int</span> data)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.data = data;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">decrease</span><span class="params">(<span class="type">int</span> amount)</span> &#123;</span><br><span class="line">        <span class="type">int</span> oldValue;</span><br><span class="line">        <span class="keyword">while</span>(<span class="literal">true</span>) &#123;</span><br><span class="line">            <span class="comment">// 获取共享变量旧值</span></span><br><span class="line">            oldValue = data;</span><br><span class="line">            <span class="comment">// cas 尝试修改 data 为 旧值 + amount，如果期间旧值被别的线程改了，返回 false</span></span><br><span class="line">            <span class="keyword">if</span> (unsafe.compareAndSwapInt(<span class="built_in">this</span>, DATA_OFFSET, oldValue, oldValue - amount)) &#123;</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getData</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> data;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h2 id="共享模型之不可变"><a href="#共享模型之不可变" class="headerlink" title="共享模型之不可变"></a>共享模型之不可变</h2><p>不可变即使共享也是线程安全</p>
<h3 id="日期转换的问题"><a href="#日期转换的问题" class="headerlink" title="日期转换的问题"></a>日期转换的问题</h3><p>下面的代码在运行时，由于 SimpleDateFormat 不是线程安全的</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">SimpleDateFormat</span> <span class="variable">sdf</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SimpleDateFormat</span>(<span class="string">&quot;yyyy-MM-dd&quot;</span>);</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) &#123;</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            log.debug(<span class="string">&quot;&#123;&#125;&quot;</span>, sdf.parse(<span class="string">&quot;1951-04-21&quot;</span>));</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            log.error(<span class="string">&quot;&#123;&#125;&quot;</span>, e);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;).start();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>有很大几率出现 java.lang.NumberFormatException 或者出现不正确的日期解析结果</p>
<p>思路 - 同步锁：这样虽能解决问题，但带来的是性能上的损失</p>
<p>思路 - 不可变：如果一个对象在不能够修改其内部状态（属性），那么它就是线程安全的，因为不存在并发修改！这样的对象在 Java 中有很多，例如在 Java 8 后，提供了一个新的日期格式化类：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">DateTimeFormatter</span> <span class="variable">dtf</span> <span class="operator">=</span> DateTimeFormatter.ofPattern(<span class="string">&quot;yyyy-MM-dd&quot;</span>);</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) &#123;</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        <span class="type">LocalDate</span> <span class="variable">date</span> <span class="operator">=</span> dtf.parse(<span class="string">&quot;2018-10-01&quot;</span>, LocalDate::from);</span><br><span class="line">        log.debug(<span class="string">&quot;&#123;&#125;&quot;</span>, date);</span><br><span class="line">    &#125;).start();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>可以看 DateTimeFormatter 的文档：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@implSpec</span></span><br><span class="line">This <span class="keyword">class</span> <span class="title class_">is</span> immutable and thread-safe.</span><br></pre></td></tr></table></figure>

<p>不可变对象是另一种避免竞争的方式</p>
<h3 id="不可变设计"><a href="#不可变设计" class="headerlink" title="不可变设计"></a>不可变设计</h3><h4 id="final-的使用"><a href="#final-的使用" class="headerlink" title="final 的使用"></a>final 的使用</h4><p>类、类中所有属性都是 final 的</p>
<ul>
<li>属性用 final 修饰保证了该属性是只读的，不能修改</li>
<li>类用 final 修饰保证了该类中的方法不能被覆盖，防止子类无意间破坏不可变性</li>
</ul>
<h5 id="final原理"><a href="#final原理" class="headerlink" title="final原理"></a>final原理</h5><ol>
<li>设置 final 变量的原理</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TestFinal</span> &#123;</span><br><span class="line">    <span class="keyword">final</span> <span class="type">int</span> <span class="variable">a</span> <span class="operator">=</span> <span class="number">20</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//字节码</span></span><br><span class="line"><span class="number">0</span>: aload_0</span><br><span class="line"><span class="number">1</span>: invokespecial #<span class="number">1</span> <span class="comment">// Method java/lang/Object.&quot;&lt;init&gt;&quot;:()V</span></span><br><span class="line"><span class="number">4</span>: aload_0</span><br><span class="line"><span class="number">5</span>: bipush <span class="number">20</span></span><br><span class="line"><span class="number">7</span>: putfield #<span class="number">2</span> <span class="comment">// Field a:I</span></span><br><span class="line"> &lt;-- 写屏障</span><br><span class="line"><span class="number">10</span>: <span class="keyword">return</span></span><br></pre></td></tr></table></figure>

<p>发现 final 变量的赋值也会通过 putfield 指令来完成，同样在这条指令之后也会加入写屏障，保证在其它线程读到它的值时不会出现为 0 的情况</p>
<ol start="2">
<li>获取 final 变量的原理</li>
</ol>
<p>final变量的值在编译期会复制一份，复制到要调用的其他类中</p>
<h4 id="保护性拷贝"><a href="#保护性拷贝" class="headerlink" title="保护性拷贝"></a>保护性拷贝</h4><p>使用字符串时，也有一些跟修改相关的方法，以 substring 为例：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> String <span class="title function_">substring</span><span class="params">(<span class="type">int</span> beginIndex)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (beginIndex &lt; <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">StringIndexOutOfBoundsException</span>(beginIndex);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">int</span> <span class="variable">subLen</span> <span class="operator">=</span> value.length - beginIndex;</span><br><span class="line">    <span class="keyword">if</span> (subLen &lt; <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">StringIndexOutOfBoundsException</span>(subLen);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> (beginIndex == <span class="number">0</span>) ? <span class="built_in">this</span> : <span class="keyword">new</span> <span class="title class_">String</span>(value, beginIndex, subLen);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">public</span> <span class="title function_">String</span><span class="params">(<span class="type">char</span> value[], <span class="type">int</span> offset, <span class="type">int</span> count)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (offset &lt; <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">StringIndexOutOfBoundsException</span>(offset);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (count &lt;= <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (count &lt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">StringIndexOutOfBoundsException</span>(count);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (offset &lt;= value.length) &#123;</span><br><span class="line">            <span class="built_in">this</span>.value = <span class="string">&quot;&quot;</span>.value;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (offset &gt; value.length - count) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">StringIndexOutOfBoundsException</span>(offset + count);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="built_in">this</span>.value = Arrays.copyOfRange(value, offset, offset+count);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>构造新字符串对象时，会生成新的 char[] value，Arrays.copyOfRange()对内容进行复制 。这种通过创建副本对象来避免共享的手段称之为【保护性拷贝（defensive copy）】</p>
<blockquote>
<p>注意：</p>
<p>虽然Integer是不可变线程安全的，但是为什么要用到原子类？之前的转账方法中涉及三个操作，读取变量，运算，变量赋值三步操作，虽然每一步都是线程安全但多个操作不是原子的</p>
</blockquote>
<h3 id="无状态"><a href="#无状态" class="headerlink" title="无状态"></a>无状态</h3><p>在 web 阶段学习时，设计 Servlet 时为了保证其线程安全，都会有这样的建议，不要为 Servlet 设置成员变量，这种没有任何成员变量的类是线程安全的</p>
<blockquote>
<p>因为成员变量保存的数据也可以称为状态信息，因此没有成员变量就称之为【无状态】</p>
</blockquote>
<h2 id="共享模型之并发工具"><a href="#共享模型之并发工具" class="headerlink" title="共享模型之并发工具"></a>共享模型之并发工具</h2><h3 id="线程池"><a href="#线程池" class="headerlink" title="线程池"></a>线程池</h3><h4 id="自定义线程池"><a href="#自定义线程池" class="headerlink" title="自定义线程池"></a>自定义线程池</h4><p>线程池相当于消费者</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220416153327099.png" alt="image-20220416153327099"></p>
<p>步骤1：自定义拒绝策略接口</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@FunctionalInterface</span> <span class="comment">// 拒绝策略</span></span><br><span class="line"><span class="keyword">interface</span> <span class="title class_">RejectPolicy</span>&lt;T&gt; &#123;</span><br><span class="line">    <span class="keyword">void</span> <span class="title function_">reject</span><span class="params">(BlockingQueue&lt;T&gt; queue, T task)</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>步骤1：自定义任务队列</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">BlockingQueue</span>&lt;T&gt; &#123;</span><br><span class="line">    <span class="comment">// 1. 任务队列</span></span><br><span class="line">    <span class="keyword">private</span> Deque&lt;T&gt; queue = <span class="keyword">new</span> <span class="title class_">ArrayDeque</span>&lt;&gt;();</span><br><span class="line">    <span class="comment">// 2. 锁</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">ReentrantLock</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReentrantLock</span>();</span><br><span class="line">    <span class="comment">// 3. 生产者条件变量</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">Condition</span> <span class="variable">fullWaitSet</span> <span class="operator">=</span> lock.newCondition();</span><br><span class="line">    <span class="comment">// 4. 消费者条件变量</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">Condition</span> <span class="variable">emptyWaitSet</span> <span class="operator">=</span> lock.newCondition();</span><br><span class="line">    <span class="comment">// 5. 容量</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> capcity;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">BlockingQueue</span><span class="params">(<span class="type">int</span> capcity)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.capcity = capcity;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 阻塞获取</span></span><br><span class="line">    <span class="keyword">public</span> T <span class="title function_">pull</span><span class="params">()</span> &#123;</span><br><span class="line">        lock.lock();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">while</span> (queue.isEmpty()) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    emptyWaitSet.await();</span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="type">T</span> <span class="variable">t</span> <span class="operator">=</span> queue.removeFirst();</span><br><span class="line">            fullWaitSet.signal();</span><br><span class="line">            <span class="keyword">return</span> t;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            lock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 阻塞添加</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">put</span><span class="params">(T task)</span> &#123;</span><br><span class="line">        lock.lock();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">while</span> (queue.size() == capcity) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    log.debug(<span class="string">&quot;等待加入任务队列 &#123;&#125; ...&quot;</span>, task);</span><br><span class="line">                    fullWaitSet.await();</span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            log.debug(<span class="string">&quot;加入任务队列 &#123;&#125;&quot;</span>, task);</span><br><span class="line">            queue.addLast(task);</span><br><span class="line">            emptyWaitSet.signal();</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            lock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 带超时阻塞获取</span></span><br><span class="line">    <span class="keyword">public</span> T <span class="title function_">poll</span><span class="params">(<span class="type">long</span> timeout, TimeUnit unit)</span> &#123;</span><br><span class="line">        lock.lock();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 将 timeout 统一转换为 纳秒</span></span><br><span class="line">            <span class="type">long</span> <span class="variable">nanos</span> <span class="operator">=</span> unit.toNanos(timeout);</span><br><span class="line">            <span class="keyword">while</span> (queue.isEmpty()) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="comment">// 返回值是剩余时间</span></span><br><span class="line">                    <span class="keyword">if</span> (nanos &lt;= <span class="number">0</span>) &#123;</span><br><span class="line">                        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="comment">//awaitNanos已经封装好了方法，返回的就是剩余的时间</span></span><br><span class="line">                    nanos = emptyWaitSet.awaitNanos(nanos);</span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="type">T</span> <span class="variable">t</span> <span class="operator">=</span> queue.removeFirst();</span><br><span class="line">            fullWaitSet.signal();</span><br><span class="line">            <span class="keyword">return</span> t;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            lock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 带超时时间阻塞添加</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">put</span><span class="params">(T task, <span class="type">long</span> timeout, TimeUnit timeUnit)</span> &#123;</span><br><span class="line">        lock.lock();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">long</span> <span class="variable">nanos</span> <span class="operator">=</span> timeUnit.toNanos(timeout);</span><br><span class="line">            <span class="keyword">while</span> (queue.size() == capcity) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="keyword">if</span>(nanos &lt;= <span class="number">0</span>) &#123;</span><br><span class="line">                        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                    log.debug(<span class="string">&quot;等待加入任务队列 &#123;&#125; ...&quot;</span>, task);</span><br><span class="line">                    nanos = fullWaitSet.awaitNanos(nanos);</span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            log.debug(<span class="string">&quot;加入任务队列 &#123;&#125;&quot;</span>, task);</span><br><span class="line">            queue.addLast(task);</span><br><span class="line">            emptyWaitSet.signal();</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            lock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">size</span><span class="params">()</span> &#123;</span><br><span class="line">        lock.lock();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">return</span> queue.size();</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            lock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">tryPut</span><span class="params">(RejectPolicy&lt;T&gt; rejectPolicy, T task)</span> &#123;</span><br><span class="line">        lock.lock();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 判断队列是否满</span></span><br><span class="line">            <span class="keyword">if</span>(queue.size() == capcity) &#123;</span><br><span class="line">                rejectPolicy.reject(<span class="built_in">this</span>, task);</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123; <span class="comment">// 有空闲</span></span><br><span class="line">                log.debug(<span class="string">&quot;加入任务队列 &#123;&#125;&quot;</span>, task);</span><br><span class="line">                queue.addLast(task);</span><br><span class="line">                emptyWaitSet.signal();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            lock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>步骤3：自定义线程池</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">ThreadPool</span> &#123;</span><br><span class="line">    <span class="comment">// 任务队列</span></span><br><span class="line">    <span class="keyword">private</span> BlockingQueue&lt;Runnable&gt; taskQueue;</span><br><span class="line">    <span class="comment">// 线程集合</span></span><br><span class="line">    <span class="keyword">private</span> HashSet&lt;Worker&gt; workers = <span class="keyword">new</span> <span class="title class_">HashSet</span>&lt;&gt;();</span><br><span class="line">    <span class="comment">// 核心线程数</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> coreSize;</span><br><span class="line">    <span class="comment">// 获取任务时的超时时间</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">long</span> timeout;</span><br><span class="line">    <span class="keyword">private</span> TimeUnit timeUnit;</span><br><span class="line">    <span class="keyword">private</span> RejectPolicy&lt;Runnable&gt; rejectPolicy;</span><br><span class="line">    <span class="comment">// 执行任务</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">execute</span><span class="params">(Runnable task)</span> &#123;</span><br><span class="line">        <span class="comment">// 当任务数没有超过 coreSize 时，直接交给 worker 对象执行</span></span><br><span class="line">        <span class="comment">// 如果任务数超过 coreSize 时，加入任务队列暂存</span></span><br><span class="line">        <span class="keyword">synchronized</span> (workers) &#123;</span><br><span class="line">            <span class="keyword">if</span>(workers.size() &lt; coreSize) &#123;</span><br><span class="line">                <span class="type">Worker</span> <span class="variable">worker</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Worker</span>(task);</span><br><span class="line">                log.debug(<span class="string">&quot;新增 worker&#123;&#125;, &#123;&#125;&quot;</span>, worker, task);</span><br><span class="line">                workers.add(worker);</span><br><span class="line">                worker.start();</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                taskQueue.tryPut(rejectPolicy, task);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">ThreadPool</span><span class="params">(<span class="type">int</span> coreSize, <span class="type">long</span> timeout, TimeUnit timeUnit, <span class="type">int</span> queueCapcity, RejectPolicy&lt;Runnable&gt; rejectPolicy)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.coreSize = coreSize;</span><br><span class="line">        <span class="built_in">this</span>.timeout = timeout;</span><br><span class="line">        <span class="built_in">this</span>.timeUnit = timeUnit;</span><br><span class="line">        <span class="built_in">this</span>.taskQueue = <span class="keyword">new</span> <span class="title class_">BlockingQueue</span>&lt;&gt;(queueCapcity);</span><br><span class="line">        <span class="built_in">this</span>.rejectPolicy = rejectPolicy;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">class</span> <span class="title class_">Worker</span> <span class="keyword">extends</span> <span class="title class_">Thread</span>&#123;</span><br><span class="line">        <span class="keyword">private</span> Runnable task;</span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">Worker</span><span class="params">(Runnable task)</span> &#123;</span><br><span class="line">            <span class="built_in">this</span>.task = task;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="comment">// 执行任务</span></span><br><span class="line">            <span class="comment">// 1) 当 task 不为空，执行任务</span></span><br><span class="line">            <span class="comment">// 2) 当 task 执行完毕，再接着从任务队列获取任务并执行</span></span><br><span class="line">            <span class="comment">// while(task != null || (task = taskQueue.take()) != null) &#123;</span></span><br><span class="line">            <span class="keyword">while</span>(task != <span class="literal">null</span> || (task = taskQueue.poll(timeout, timeUnit)) != <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    log.debug(<span class="string">&quot;正在执行...&#123;&#125;&quot;</span>, task);</span><br><span class="line">                    task.run();</span><br><span class="line">                &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                    task = <span class="literal">null</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">synchronized</span> (workers) &#123;</span><br><span class="line">                log.debug(<span class="string">&quot;worker 被移除&#123;&#125;&quot;</span>, <span class="built_in">this</span>);</span><br><span class="line">                workers.remove(<span class="built_in">this</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>步骤4：测试</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="comment">//将函数式接口的拒绝策略传进去</span></span><br><span class="line">    <span class="type">ThreadPool</span> <span class="variable">threadPool</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ThreadPool</span>(<span class="number">1</span>, <span class="number">1000</span>, TimeUnit.MILLISECONDS, <span class="number">1</span>, (queue, task)-&gt;&#123;</span><br><span class="line">        <span class="comment">// 1. 死等</span></span><br><span class="line">        <span class="comment">// queue.put(task);</span></span><br><span class="line">        <span class="comment">// 2) 带超时等待</span></span><br><span class="line">        <span class="comment">// queue.offer(task, 1500, TimeUnit.MILLISECONDS);</span></span><br><span class="line">        <span class="comment">// 3) 让调用者放弃任务执行</span></span><br><span class="line">        <span class="comment">// log.debug(&quot;放弃&#123;&#125;&quot;, task);</span></span><br><span class="line">        <span class="comment">// 4) 让调用者抛出异常</span></span><br><span class="line">        <span class="comment">// throw new RuntimeException(&quot;任务执行失败 &quot; + task);</span></span><br><span class="line">        <span class="comment">// 5) 让调用者自己执行任务</span></span><br><span class="line">        task.run();</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">4</span>; i++) &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> i;</span><br><span class="line">        threadPool.execute(() -&gt; &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                Thread.sleep(<span class="number">1000L</span>);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">            log.debug(<span class="string">&quot;&#123;&#125;&quot;</span>, j);</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="ThreadPoolExecutor"><a href="#ThreadPoolExecutor" class="headerlink" title="ThreadPoolExecutor"></a>ThreadPoolExecutor</h4><h5 id="线程池状态"><a href="#线程池状态" class="headerlink" title="线程池状态"></a>线程池状态</h5><p>ThreadPoolExecutor 使用 int 的高 3 位来表示线程池状态，低 29 位表示线程数量</p>
<table>
<thead>
<tr>
<th>状态名</th>
<th>高3位</th>
<th>接收新任务</th>
<th>处理阻塞队列任务</th>
<th>说明</th>
</tr>
</thead>
<tbody><tr>
<td>RUNNING</td>
<td>111</td>
<td>Y</td>
<td>Y</td>
<td></td>
</tr>
<tr>
<td>SHUTDOWN</td>
<td>000</td>
<td>N</td>
<td>Y</td>
<td>不会接收新任务，但会处理阻塞队列剩余 任务</td>
</tr>
<tr>
<td>STOP</td>
<td>001</td>
<td>N</td>
<td>N</td>
<td>会中断正在执行的任务，并抛弃阻塞队列 任务</td>
</tr>
<tr>
<td>TIDYING</td>
<td>010</td>
<td>-</td>
<td>-</td>
<td>任务全执行完毕，活动线程为 0 即将进入 终结</td>
</tr>
<tr>
<td>TERMINATED</td>
<td>011</td>
<td>-</td>
<td>-</td>
<td>终结状态</td>
</tr>
</tbody></table>
<p>从数字上比较，TERMINATED &gt; TIDYING &gt; STOP &gt; SHUTDOWN &gt; RUNNING</p>
<p>这些信息存储在一个原子变量 ctl 中，目的是<strong>将线程池状态与线程个数合二为一，这样就可以用一次 cas 原子操作进行赋值</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// c 为旧值， ctlOf 返回结果为新值</span></span><br><span class="line">ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))));</span><br><span class="line"><span class="comment">// rs 为高 3 位代表线程池状态， wc 为低 29 位代表线程个数，ctl 是合并它们</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="type">int</span> <span class="title function_">ctlOf</span><span class="params">(<span class="type">int</span> rs, <span class="type">int</span> wc)</span> &#123; <span class="keyword">return</span> rs | wc; &#125;</span><br></pre></td></tr></table></figure>

<h5 id="构造方法"><a href="#构造方法" class="headerlink" title="构造方法"></a>构造方法</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="title function_">ThreadPoolExecutor</span><span class="params">(<span class="type">int</span> corePoolSize,</span></span><br><span class="line"><span class="params">                          <span class="type">int</span> maximumPoolSize,</span></span><br><span class="line"><span class="params">                          <span class="type">long</span> keepAliveTime,</span></span><br><span class="line"><span class="params">                          TimeUnit unit,</span></span><br><span class="line"><span class="params">                          BlockingQueue&lt;Runnable&gt; workQueue,</span></span><br><span class="line"><span class="params">                          ThreadFactory threadFactory,</span></span><br><span class="line"><span class="params">                          RejectedExecutionHandler handler)</span></span><br></pre></td></tr></table></figure>

<ul>
<li>corePoolSize 核心线程数目 (最多保留的线程数)</li>
<li>maximumPoolSize 最大线程数目</li>
<li>keepAliveTime 生存时间 - 针对救急线程</li>
<li>unit 时间单位 - 针对救急线程</li>
<li>workQueue 阻塞队列</li>
<li>threadFactory 线程工厂 - 可以为线程创建时起个好名字</li>
<li>handler 拒绝策略</li>
</ul>
<ul>
<li>线程池中刚开始没有线程，当一个任务提交给线程池后，线程池会创建一个新线程来执行任务。</li>
<li>当线程数达到 corePoolSize 并没有线程空闲，这时再加入任务，新加的任务会被加入workQueue 队列排 队，直到有空闲的线程。 </li>
<li>如果队列选择了有界队列，那么任务超过了队列大小时，会创建 maximumPoolSize - corePoolSize 数目的线程来救急。（有界队列才会有救急线程） </li>
<li>如果线程到达 maximumPoolSize 仍然有新任务这时会执行拒绝策略。拒绝策略 jdk 提供了 4 种实现，其它著名框架也提供了实现<ul>
<li>AbortPolicy 让调用者抛出 RejectedExecutionException 异常，这是默认策略</li>
<li>CallerRunsPolicy 让调用者运行任务</li>
<li>DiscardPolicy 放弃本次任务</li>
<li>DiscardOldestPolicy 放弃队列中最早的任务，本任务取而代之</li>
<li>Dubbo 的实现，在抛出 RejectedExecutionException 异常之前会记录日志，并 dump 线程栈信息，方便定位问题</li>
<li>Netty 的实现，是创建一个新线程来执行任务</li>
<li>ActiveMQ 的实现，带超时等待（60s）尝试放入队列</li>
<li>PinPoint 的实现，它使用了一个拒绝策略链，会逐一尝试策略链中每种拒绝策略</li>
</ul>
</li>
<li>当高峰过去后，超过corePoolSize的救急线程如果一段时间没有任务做，需要结束节省资源，这个时间由 keepAliveTime 和 unit 来控制。</li>
</ul>
<p>根据这个构造方法，JDK Executors 类中提供了众多工厂方法来创建各种用途的线程池</p>
<h5 id="newFixedThreadPool"><a href="#newFixedThreadPool" class="headerlink" title="newFixedThreadPool"></a>newFixedThreadPool</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> ExecutorService <span class="title function_">newFixedThreadPool</span><span class="params">(<span class="type">int</span> nThreads)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">ThreadPoolExecutor</span>(nThreads, nThreads,</span><br><span class="line">                                  <span class="number">0L</span>, TimeUnit.MILLISECONDS,</span><br><span class="line">                                  <span class="keyword">new</span> <span class="title class_">LinkedBlockingQueue</span>&lt;Runnable&gt;());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>特点</p>
<ul>
<li>核心线程数 == 最大线程数（没有救急线程被创建），因此也无需超时时间</li>
<li>阻塞队列是无界的，可以放任意数量的任务</li>
</ul>
<blockquote>
<p>适用于任务量已知，相对耗时的任务</p>
</blockquote>
<h5 id="newCachedThreadPool"><a href="#newCachedThreadPool" class="headerlink" title="newCachedThreadPool"></a>newCachedThreadPool</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> ExecutorService <span class="title function_">newCachedThreadPool</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">ThreadPoolExecutor</span>(<span class="number">0</span>, Integer.MAX_VALUE,</span><br><span class="line">                                  <span class="number">60L</span>, TimeUnit.SECONDS,</span><br><span class="line">                                  <span class="keyword">new</span> <span class="title class_">SynchronousQueue</span>&lt;Runnable&gt;());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>特点</p>
<ul>
<li>核心线程数是 0， 最大线程数是 Integer.MAX_VALUE，救急线程的空闲生存时间是 60s，意味着全部都是救急线程（60s 后可以回收）,救急线程可以无限创建</li>
<li>队列采用了 SynchronousQueue 实现特点是，它没有容量，没有线程来取是放不进去的（一手交钱、一手交货）</li>
</ul>
<blockquote>
<p>整个线程池表现为线程数会根据任务量不断增长，没有上限，当任务执行完毕，空闲 1分钟后释放线程。 适合任务数比较密集，但每个任务执行时间较短的情况</p>
</blockquote>
<h5 id="newSingleThreadExecutor"><a href="#newSingleThreadExecutor" class="headerlink" title="newSingleThreadExecutor"></a>newSingleThreadExecutor</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> ExecutorService <span class="title function_">newSingleThreadExecutor</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">FinalizableDelegatedExecutorService</span></span><br><span class="line">        (<span class="keyword">new</span> <span class="title class_">ThreadPoolExecutor</span>(<span class="number">1</span>, <span class="number">1</span>,</span><br><span class="line">                                <span class="number">0L</span>, TimeUnit.MILLISECONDS,</span><br><span class="line">                                <span class="keyword">new</span> <span class="title class_">LinkedBlockingQueue</span>&lt;Runnable&gt;()));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>使用场景： </p>
<p>希望多个任务排队执行。线程数固定为 1，任务数多于 1 时，会放入无界队列排队。任务执行完毕，这唯一的线程 也不会被释放。 </p>
<p>区别： </p>
<ul>
<li>自己创建一个单线程串行执行任务，如果任务执行失败而终止那么没有任何补救措施，而线程池还会新建一个线程，保证池的正常工作 </li>
<li>Executors.newSingleThreadExecutor() 线程个数始终为1，不能修改 <ul>
<li>FinalizableDelegatedExecutorService 应用的是装饰器模式，只对外暴露了 ExecutorService 接口，因此不能调用 ThreadPoolExecutor 中特有的方法</li>
</ul>
</li>
<li> Executors.newFixedThreadPool(1) 初始时为1，以后还可以修改</li>
<li>对外暴露的是 ThreadPoolExecutor 对象，可以强转后调用 setCorePoolSize 等方法进行修改</li>
</ul>
<h5 id="提交任务"><a href="#提交任务" class="headerlink" title="提交任务"></a>提交任务</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 执行任务</span></span><br><span class="line"><span class="keyword">void</span> <span class="title function_">execute</span><span class="params">(Runnable command)</span>;</span><br><span class="line"><span class="comment">// 提交任务 task，用返回值 Future 获得任务执行结果</span></span><br><span class="line">&lt;T&gt; Future&lt;T&gt; <span class="title function_">submit</span><span class="params">(Callable&lt;T&gt; task)</span>;</span><br><span class="line"><span class="comment">// 提交 tasks 中所有任务</span></span><br><span class="line">&lt;T&gt; List&lt;Future&lt;T&gt;&gt; <span class="title function_">invokeAll</span><span class="params">(Collection&lt;? extends Callable&lt;T&gt;&gt; tasks)</span></span><br><span class="line">    <span class="keyword">throws</span> InterruptedException;</span><br><span class="line"><span class="comment">// 提交 tasks 中所有任务，带超时时间</span></span><br><span class="line">&lt;T&gt; List&lt;Future&lt;T&gt;&gt; <span class="title function_">invokeAll</span><span class="params">(Collection&lt;? extends Callable&lt;T&gt;&gt; tasks,</span></span><br><span class="line"><span class="params">                              <span class="type">long</span> timeout, TimeUnit unit)</span></span><br><span class="line">    <span class="keyword">throws</span> InterruptedException;</span><br><span class="line"><span class="comment">// 提交 tasks 中所有任务，哪个任务先成功执行完毕，返回此任务执行结果，其它任务取消</span></span><br><span class="line">&lt;T&gt; T <span class="title function_">invokeAny</span><span class="params">(Collection&lt;? extends Callable&lt;T&gt;&gt; tasks)</span></span><br><span class="line">    <span class="keyword">throws</span> InterruptedException, ExecutionException;</span><br><span class="line"><span class="comment">// 提交 tasks 中所有任务，哪个任务先成功执行完毕，返回此任务执行结果，其它任务取消，带超时时间</span></span><br><span class="line">&lt;T&gt; T <span class="title function_">invokeAny</span><span class="params">(Collection&lt;? extends Callable&lt;T&gt;&gt; tasks,</span></span><br><span class="line"><span class="params">                <span class="type">long</span> timeout, TimeUnit unit)</span></span><br><span class="line">    <span class="keyword">throws</span> InterruptedException, ExecutionException, TimeoutException;</span><br></pre></td></tr></table></figure>

<h5 id="关闭线程池"><a href="#关闭线程池" class="headerlink" title="关闭线程池"></a>关闭线程池</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> <span class="title function_">shutdown</span><span class="params">()</span>;<span class="comment">//线程池状态变为 SHUTDOWN</span></span><br><span class="line">List&lt;Runnable&gt; <span class="title function_">shutdownNow</span><span class="params">()</span>;<span class="comment">//线程池状态变为 STOP</span></span><br></pre></td></tr></table></figure>

<h5 id="任务调度线程池"><a href="#任务调度线程池" class="headerlink" title="任务调度线程池"></a>任务调度线程池</h5><p>在『任务调度线程池』功能加入之前，可以使用 java.util.Timer 来实现定时功能，Timer 的优点在于简单易用，但 由于所有任务都是由同一个线程来调度，因此所有任务都是串行执行的，同一时间只能有一个任务在执行，前一个 任务的延迟或异常都将会影响到之后的任务。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="type">Timer</span> <span class="variable">timer</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Timer</span>();</span><br><span class="line">    <span class="type">TimerTask</span> <span class="variable">task1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TimerTask</span>() &#123;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">            log.debug(<span class="string">&quot;task 1&quot;</span>);</span><br><span class="line">            sleep(<span class="number">2</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;;</span><br><span class="line">    <span class="type">TimerTask</span> <span class="variable">task2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TimerTask</span>() &#123;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">            log.debug(<span class="string">&quot;task 2&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;;</span><br><span class="line">    <span class="comment">// 使用 timer 添加两个任务，希望它们都在 1s 后执行</span></span><br><span class="line">    <span class="comment">// 但由于 timer 内只有一个线程来顺序执行队列中的任务，因此『任务1』的延时，影响了『任务2』的执行</span></span><br><span class="line">    timer.schedule(task1, <span class="number">1000</span>);</span><br><span class="line">    timer.schedule(task2, <span class="number">1000</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>使用 ScheduledExecutorService 改写：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">ScheduledExecutorService</span> <span class="variable">executor</span> <span class="operator">=</span>Executors.newScheduledThreadPool(<span class="number">2</span>);</span><br><span class="line"><span class="comment">// 添加两个任务，希望它们都在 1s 后执行</span></span><br><span class="line">executor.schedule(() -&gt; &#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;任务1，执行时间：&quot;</span> + <span class="keyword">new</span> <span class="title class_">Date</span>());</span><br><span class="line">    <span class="keyword">try</span> &#123; Thread.sleep(<span class="number">2000</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; &#125;</span><br><span class="line">&#125;, <span class="number">1000</span>, TimeUnit.MILLISECONDS);</span><br><span class="line">executor.schedule(() -&gt; &#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;任务2，执行时间：&quot;</span> + <span class="keyword">new</span> <span class="title class_">Date</span>());</span><br><span class="line">&#125;, <span class="number">1000</span>, TimeUnit.MILLISECONDS);</span><br></pre></td></tr></table></figure>

<p>scheduleAtFixedRate ：定速率执行，隔一段时间执行一次，适用于需要多次执行的任务</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">ScheduledExecutorService</span> <span class="variable">pool</span> <span class="operator">=</span> Executors.newScheduledThreadPool(<span class="number">1</span>);</span><br><span class="line">log.debug(<span class="string">&quot;start...&quot;</span>);</span><br><span class="line">pool.scheduleAtFixedRate(() -&gt; &#123;</span><br><span class="line">    log.debug(<span class="string">&quot;running...&quot;</span>);</span><br><span class="line">&#125;, <span class="number">1</span>, <span class="number">1</span>, TimeUnit.SECONDS);</span><br></pre></td></tr></table></figure>

<p>scheduleWithFixedDelay ：的间隔是 上一个任务结束 &lt;-&gt; 延时 &lt;-&gt; 下一个任务开始</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">ScheduledExecutorService</span> <span class="variable">pool</span> <span class="operator">=</span> Executors.newScheduledThreadPool(<span class="number">1</span>);</span><br><span class="line">log.debug(<span class="string">&quot;start...&quot;</span>);</span><br><span class="line">pool.scheduleWithFixedDelay(()-&gt; &#123;</span><br><span class="line">    log.debug(<span class="string">&quot;running...&quot;</span>);</span><br><span class="line">    sleep(<span class="number">2</span>);</span><br><span class="line">&#125;, <span class="number">1</span>, <span class="number">1</span>, TimeUnit.SECONDS);</span><br></pre></td></tr></table></figure>

<h5 id="正确处理执行任务异常"><a href="#正确处理执行任务异常" class="headerlink" title="正确处理执行任务异常"></a>正确处理执行任务异常</h5><p>方法1：主动捉异常，任务自身处理异常</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">ExecutorService</span> <span class="variable">pool</span> <span class="operator">=</span> Executors.newFixedThreadPool(<span class="number">1</span>);</span><br><span class="line">pool.submit(() -&gt; &#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        log.debug(<span class="string">&quot;task1&quot;</span>);</span><br><span class="line">        <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span> / <span class="number">0</span>;</span><br><span class="line">    &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">        log.error(<span class="string">&quot;error:&quot;</span>, e);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<p>方法2：使用 Future，get时会返回异常信息</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">ExecutorService</span> <span class="variable">pool</span> <span class="operator">=</span> Executors.newFixedThreadPool(<span class="number">1</span>);</span><br><span class="line">Future&lt;Boolean&gt; f = pool.submit(() -&gt; &#123;</span><br><span class="line">    log.debug(<span class="string">&quot;task1&quot;</span>);</span><br><span class="line">    <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">1</span> / <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;);</span><br><span class="line">log.debug(<span class="string">&quot;result:&#123;&#125;&quot;</span>, f.get());</span><br></pre></td></tr></table></figure>

<h5 id="Tomcat-线程池"><a href="#Tomcat-线程池" class="headerlink" title="Tomcat 线程池"></a>Tomcat 线程池</h5><p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220417125508384.png" alt="image-20220417125508384"></p>
<ul>
<li>LimitLatch 用来限流，可以控制最大连接个数，类似 J.U.C 中的 Semaphore</li>
<li>Acceptor 只负责【接收新的 socket 连接】</li>
<li>Poller 只负责监听 socket channel 是否有【可读的 I/O 事件】 </li>
<li>一旦可读，封装一个任务对象（socketProcessor），提交给 Executor 线程池处理 </li>
<li>Executor 线程池中的工作线程最终负责【处理请求】</li>
</ul>
<p>Tomcat 线程池扩展了 ThreadPoolExecutor，行为稍有不同如果总线程数达到 maximumPoolSize，这时不会立刻抛 RejectedExecutionException 异常，而是再次尝试将任务放入队列，如果还失败，才抛出RejectedExecutionException 异常</p>
<p>源码 tomcat-7.0.42</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">execute</span><span class="params">(Runnable command, <span class="type">long</span> timeout, TimeUnit unit)</span> &#123;</span><br><span class="line">    submittedCount.incrementAndGet();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="built_in">super</span>.execute(command);</span><br><span class="line">    &#125; <span class="keyword">catch</span> (RejectedExecutionException rx) &#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">super</span>.getQueue() <span class="keyword">instanceof</span> TaskQueue) &#123;</span><br><span class="line">            <span class="keyword">final</span> <span class="type">TaskQueue</span> <span class="variable">queue</span> <span class="operator">=</span> (TaskQueue)<span class="built_in">super</span>.getQueue();</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="keyword">if</span> (!queue.force(command, timeout, unit)) &#123;</span><br><span class="line">                    submittedCount.decrementAndGet();</span><br><span class="line">                    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RejectedExecutionException</span>(<span class="string">&quot;Queue capacity is full.&quot;</span>);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException x) &#123;</span><br><span class="line">                submittedCount.decrementAndGet();</span><br><span class="line">                Thread.interrupted();</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RejectedExecutionException</span>(x);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            submittedCount.decrementAndGet();</span><br><span class="line">            <span class="keyword">throw</span> rx;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>TaskQueue.java</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">force</span><span class="params">(Runnable o, <span class="type">long</span> timeout, TimeUnit unit)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">    <span class="keyword">if</span> ( parent.isShutdown() ) </span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RejectedExecutionException</span>(</span><br><span class="line">        <span class="string">&quot;Executor not running, can&#x27;t force a command into the queue&quot;</span></span><br><span class="line">    );</span><br><span class="line">    <span class="keyword">return</span> <span class="built_in">super</span>.offer(o,timeout,unit); <span class="comment">//forces the item onto the queue, to be used if the task </span></span><br><span class="line">    is rejected</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>Connector 配置</p>
<table>
<thead>
<tr>
<th>配置项</th>
<th>默认值</th>
<th>说明</th>
</tr>
</thead>
<tbody><tr>
<td>acceptorThreadCount</td>
<td>1</td>
<td>acceptor 线程数量</td>
</tr>
<tr>
<td>pollerThreadCount</td>
<td>1</td>
<td>poller 线程数量</td>
</tr>
<tr>
<td>minSpareThreads</td>
<td>10</td>
<td>核心线程数，即 corePoolSize</td>
</tr>
<tr>
<td>maxThreads</td>
<td>200</td>
<td>最大线程数，即 maximumPoolSize</td>
</tr>
<tr>
<td>executor</td>
<td>-</td>
<td>Executor 名称，用来引用下面的 Executor</td>
</tr>
</tbody></table>
<p>Executor 线程配置</p>
<table>
<thead>
<tr>
<th>配置项</th>
<th>默认值</th>
<th>说明</th>
</tr>
</thead>
<tbody><tr>
<td>threadPriority</td>
<td>5</td>
<td>线程优先级</td>
</tr>
<tr>
<td>daemon</td>
<td>true</td>
<td>线程优先级</td>
</tr>
<tr>
<td>minSpareThreads</td>
<td>25</td>
<td>线程优先级</td>
</tr>
<tr>
<td>maxThreads</td>
<td>200</td>
<td>最大线程数，即 maximumPoolSize</td>
</tr>
<tr>
<td>maxIdleTime</td>
<td>60000</td>
<td>线程生存时间，单位是毫秒，默认值即 1 分钟</td>
</tr>
<tr>
<td>maxQueueSize</td>
<td>Integer.MAX_VALUE</td>
<td>队列长度</td>
</tr>
<tr>
<td>prestartminSpareThreads</td>
<td>false</td>
<td>核心线程是否在服务器启动时启动</td>
</tr>
</tbody></table>
<h5 id="应用-1"><a href="#应用-1" class="headerlink" title="应用"></a>应用</h5><p>如何让每周四 18:00:00 定时执行任务</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 获得当前时间</span></span><br><span class="line"><span class="type">LocalDateTime</span> <span class="variable">now</span> <span class="operator">=</span> LocalDateTime.now();</span><br><span class="line"><span class="comment">// 获取本周四 18:00:00.000</span></span><br><span class="line"><span class="type">LocalDateTime</span> <span class="variable">thursday</span> <span class="operator">=</span> now.with(DayOfWeek.THURSDAY).withHour(<span class="number">18</span>).withMinute(<span class="number">0</span>).withSecond(<span class="number">0</span>).withNano(<span class="number">0</span>);</span><br><span class="line"><span class="comment">// 如果当前时间已经超过 本周四 18:00:00.000， 那么找下周四 18:00:00.000</span></span><br><span class="line"><span class="keyword">if</span>(now.compareTo(thursday) &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">    thursday = thursday.plusWeeks(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 计算时间差，即延时执行时间</span></span><br><span class="line"><span class="type">long</span> <span class="variable">initialDelay</span> <span class="operator">=</span> Duration.between(now, thursday).toMillis();</span><br><span class="line"><span class="comment">// 计算间隔时间，即 1 周的毫秒值</span></span><br><span class="line"><span class="type">long</span> <span class="variable">oneWeek</span> <span class="operator">=</span> <span class="number">7</span> * <span class="number">24</span> * <span class="number">3600</span> * <span class="number">1000</span>;</span><br><span class="line"><span class="type">ScheduledExecutorService</span> <span class="variable">executor</span> <span class="operator">=</span> Executors.newScheduledThreadPool(<span class="number">2</span>);</span><br><span class="line">System.out.println(<span class="string">&quot;开始时间：&quot;</span> + <span class="keyword">new</span> <span class="title class_">Date</span>());</span><br><span class="line">executor.scheduleAtFixedRate(() -&gt; &#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;执行时间：&quot;</span> + <span class="keyword">new</span> <span class="title class_">Date</span>());</span><br><span class="line">&#125;, initialDelay, oneWeek, TimeUnit.MILLISECONDS);</span><br></pre></td></tr></table></figure>

<h4 id="Fork-Join"><a href="#Fork-Join" class="headerlink" title="Fork/Join"></a>Fork/Join</h4><h5 id="概念-1"><a href="#概念-1" class="headerlink" title="概念"></a>概念</h5><p>Fork/Join 是 JDK 1.7 加入的新的线程池实现，它体现的是一种分治思想，适用于能够进行任务拆分的 cpu 密集型运算</p>
<p>所谓的任务拆分，是将一个大任务拆分为算法上相同的小任务，直至不能拆分可以直接求解。跟递归相关的一些计 算，如归并排序、斐波那契数列、都可以用分治思想进行求解</p>
<p>Fork/Join 在分治的基础上加入了多线程，可以把每个任务的分解和合并交给不同的线程来完成，进一步提升了运算效率 Fork/Join 默认会创建与 cpu 核心数大小相同的线程池</p>
<h5 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h5><p>提交给 Fork/Join 线程池的任务需要继承 RecursiveTask（有返回值）或 RecursiveAction（没有返回值），例如下面定义了一个对 1~n 之间的整数求和的任务</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">AddTask1</span> <span class="keyword">extends</span> <span class="title class_">RecursiveTask</span>&lt;Integer&gt; &#123;</span><br><span class="line">    <span class="type">int</span> n;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">AddTask1</span><span class="params">(<span class="type">int</span> n)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.n = n;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">toString</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="string">&quot;&#123;&quot;</span> + n + <span class="string">&#x27;&#125;&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> Integer <span class="title function_">compute</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 如果 n 已经为 1，可以求得结果了</span></span><br><span class="line">        <span class="keyword">if</span> (n == <span class="number">1</span>) &#123;</span><br><span class="line">            log.debug(<span class="string">&quot;join() &#123;&#125;&quot;</span>, n);</span><br><span class="line">            <span class="keyword">return</span> n;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 将任务进行拆分(fork)</span></span><br><span class="line">        <span class="type">AddTask1</span> <span class="variable">t1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AddTask1</span>(n - <span class="number">1</span>);</span><br><span class="line">        t1.fork();</span><br><span class="line">        log.debug(<span class="string">&quot;fork() &#123;&#125; + &#123;&#125;&quot;</span>, n, t1);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 合并(join)结果</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">result</span> <span class="operator">=</span> n + t1.join();</span><br><span class="line">        log.debug(<span class="string">&quot;join() &#123;&#125; + &#123;&#125; = &#123;&#125;&quot;</span>, n, t1, result);</span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>然后提交给 ForkJoinPool 来执行</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="type">ForkJoinPool</span> <span class="variable">pool</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ForkJoinPool</span>(<span class="number">4</span>);</span><br><span class="line">    System.out.println(pool.invoke(<span class="keyword">new</span> <span class="title class_">AddTask1</span>(<span class="number">5</span>)));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>用图来表示</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220417155150081.png" alt="image-20220417155150081"></p>
<h3 id="JUC"><a href="#JUC" class="headerlink" title="JUC"></a>JUC</h3><h4 id="AQS"><a href="#AQS" class="headerlink" title="AQS"></a>AQS</h4><h5 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h5><p>全称是 AbstractQueuedSynchronizer，是<strong>阻塞式锁</strong>和相关的同步器工具的框架</p>
<p>特点： </p>
<ul>
<li>用 state 属性来表示资源的状态（分独占模式和共享模式），子类需要定义如何维护这个状态，控制如何获取 锁和释放锁<ul>
<li>getState - 获取 state 状态</li>
<li>setState - 设置 state 状态</li>
<li>compareAndSetState - cas 机制设置 state 状态</li>
<li>独占模式是只有一个线程能够访问资源，而共享模式可以允许多个线程访问资源 </li>
</ul>
</li>
<li>提供了基于 FIFO 的等待队列，类似于 Monitor 的 EntryList </li>
<li>条件变量来实现等待、唤醒机制，支持多个条件变量，类似于 Monitor 的 WaitSet </li>
</ul>
<p>子类主要实现这样一些方法（默认抛出 UnsupportedOperationException）</p>
<ul>
<li>tryAcquire</li>
<li>tryRelease</li>
<li>tryAcquireShared</li>
<li>tryReleaseShared</li>
<li>isHeldExclusively</li>
</ul>
<p>获取锁的姿势</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 如果获取锁失败</span></span><br><span class="line"><span class="keyword">if</span> (!tryAcquire(arg)) &#123;</span><br><span class="line">    <span class="comment">// 入队, 可以选择阻塞当前线程 park unpark</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>释放锁的姿势</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 如果释放锁成功</span></span><br><span class="line"><span class="keyword">if</span> (tryRelease(arg)) &#123;</span><br><span class="line">    <span class="comment">// 让阻塞线程恢复运行</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="实现不可重入锁"><a href="#实现不可重入锁" class="headerlink" title="实现不可重入锁"></a>实现不可重入锁</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//自定义同步器</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MySync</span> <span class="keyword">extends</span> <span class="title class_">AbstractQueuedSynchronizer</span> &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="type">boolean</span> <span class="title function_">tryAcquire</span><span class="params">(<span class="type">int</span> acquires)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (compareAndSetState(<span class="number">0</span>, <span class="number">1</span>)) &#123;</span><br><span class="line">            setExclusiveOwnerThread(Thread.currentThread());</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="type">boolean</span> <span class="title function_">tryRelease</span><span class="params">(<span class="type">int</span> acquires)</span> &#123;</span><br><span class="line">        setExclusiveOwnerThread(<span class="literal">null</span>);</span><br><span class="line">        setState(<span class="number">0</span>);<span class="comment">//state是voliate的，放在后面</span></span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">protected</span> Condition <span class="title function_">newCondition</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">ConditionObject</span>();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="type">boolean</span> <span class="title function_">isHeldExclusively</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> getState() == <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//自定义锁,有了自定义同步器，很容易复用 AQS ，实现一个功能完备的自定义锁</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MyLock</span> <span class="keyword">implements</span> <span class="title class_">Lock</span> &#123;</span><br><span class="line">    <span class="keyword">static</span> <span class="type">MySync</span> <span class="variable">sync</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MySync</span>();</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="comment">// 尝试，不成功，进入等待队列</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">lock</span><span class="params">()</span> &#123;</span><br><span class="line">        sync.acquire(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="comment">// 尝试，不成功，进入等待队列，可打断</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">lockInterruptibly</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        sync.acquireInterruptibly(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="comment">// 尝试一次，不成功返回，不进入队列</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">tryLock</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> sync.tryAcquire(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="comment">// 尝试，不成功，进入等待队列，有时限</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">tryLock</span><span class="params">(<span class="type">long</span> time, TimeUnit unit)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        <span class="keyword">return</span> sync.tryAcquireNanos(<span class="number">1</span>, unit.toNanos(time));</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="comment">// 释放锁</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">unlock</span><span class="params">()</span> &#123;</span><br><span class="line">        sync.release(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="comment">// 生成条件变量</span></span><br><span class="line">    <span class="keyword">public</span> Condition <span class="title function_">newCondition</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> sync.newCondition();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="设计"><a href="#设计" class="headerlink" title="设计"></a>设计</h5><p>AQS 要实现的功能目标</p>
<ul>
<li>阻塞版本获取锁 acquire 和非阻塞的版本尝试获取锁 tryAcquire </li>
<li>获取锁超时机制</li>
<li>通过打断取消机制</li>
<li>独占机制及共享机制</li>
<li>条件不满足时的等待机制</li>
</ul>
<p>设计 AQS 的基本思想其实很简单</p>
<p>获取锁的逻辑</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">while</span>(state 状态不允许获取) &#123;</span><br><span class="line">    <span class="keyword">if</span>(队列中还没有此线程) &#123;</span><br><span class="line">        入队并阻塞</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line">当前线程出队</span><br></pre></td></tr></table></figure>

<p>释放锁的逻辑</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>(state 状态允许了) &#123;</span><br><span class="line">    恢复阻塞的线程(s)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>要点</p>
<ul>
<li>原子维护 state 状态</li>
<li>阻塞及恢复线程</li>
<li>维护队列</li>
</ul>
<ol>
<li>state 设计</li>
</ol>
<ul>
<li><p>state 使用 volatile 配合 cas 保证其修改时的原子性</p>
</li>
<li><p>state 使用了 32bit int 来维护同步状态，因为当时使用 long 在很多平台下测试的结果并不理想</p>
</li>
</ul>
<ol start="2">
<li>阻塞恢复设计 </li>
</ol>
<ul>
<li>早期的控制线程暂停和恢复的 api 有 suspend 和 resume，但它们是不可用的，因为如果先调用的 resume  那么 suspend 将感知不到</li>
<li>解决方法是使用 park &amp; unpark 来实现线程的暂停和恢复，先 unpark 再 park 也没问题 </li>
<li>park &amp; unpark 是针对线程的，而不是针对同步器的，因此控制粒度更为精细</li>
<li>park 线程还可以通过 interrupt 打断</li>
</ul>
<ol start="3">
<li>队列设计</li>
</ol>
<ul>
<li>使用了 FIFO 先入先出队列，并不支持优先级队列</li>
<li>设计时借鉴了 CLH 队列，它是一种单向无锁队列</li>
</ul>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220417182803347.png" alt="image-20220417182803347"></p>
<p>队列中有 head 和 tail 两个指针节点，都用 volatile 修饰配合 cas 使用，每个节点有 state 维护节点状态</p>
<p>入队伪代码，只需要考虑 tail 赋值的原子性</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">do</span> &#123;</span><br><span class="line">    <span class="comment">// 原来的 tail</span></span><br><span class="line">    <span class="type">Node</span> <span class="variable">prev</span> <span class="operator">=</span> tail;</span><br><span class="line">    <span class="comment">// 用 cas 在原来 tail 的基础上改为 node</span></span><br><span class="line">&#125; <span class="keyword">while</span>(tail.compareAndSet(prev, node))</span><br></pre></td></tr></table></figure>

<p>出队伪代码</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// prev 是上一个节点</span></span><br><span class="line"><span class="keyword">while</span>((Node prev=node.prev).state != 唤醒状态) &#123;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 设置头节点</span></span><br><span class="line">head = node;</span><br></pre></td></tr></table></figure>

<p>CLH 好处：</p>
<ul>
<li>无锁，使用自旋</li>
<li>快速，无阻塞</li>
</ul>
<h5 id="主要用到-AQS-的并发工具类"><a href="#主要用到-AQS-的并发工具类" class="headerlink" title="主要用到 AQS 的并发工具类"></a>主要用到 AQS 的并发工具类</h5><p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220417171841641.png" alt="image-20220417171841641"></p>
<h4 id="ReentrantLock-1"><a href="#ReentrantLock-1" class="headerlink" title="ReentrantLock"></a>ReentrantLock</h4><p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220417171927023.png" alt="image-20220417171927023"></p>
<h5 id="非公平锁实现原理"><a href="#非公平锁实现原理" class="headerlink" title="非公平锁实现原理"></a>非公平锁实现原理</h5><p>构造器</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="title function_">ReentrantLock</span><span class="params">()</span> &#123;</span><br><span class="line">    sync = <span class="keyword">new</span> <span class="title class_">NonfairSync</span>();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>NonfairSync 继承自 AQS</p>
<p>第一个竞争出现时</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220417172315406.png" alt="image-20220417172315406"></p>
<p>Thread-1 执行了 </p>
<ol>
<li><p>CAS 尝试将 state 由 0 改为 1，结果失败 </p>
</li>
<li><p>进入 tryAcquire 逻辑，这时 state 已经是1，结果仍然失败</p>
</li>
<li><p> 接下来进入 addWaiter 逻辑，构造 Node 队列</p>
</li>
</ol>
<ul>
<li><p>图中黄色三角表示该 Node 的 waitStatus 状态，其中 0 为默认正常状态</p>
</li>
<li><p>Node 的创建是懒惰的</p>
</li>
<li><p>其中第一个 Node 称为 Dummy（哑元）或哨兵，用来占位，并不关联线程</p>
</li>
</ul>
<p>   <img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220417181958557.png" alt="image-20220417181958557"></p>
<p>当前线程进入 acquireQueued 逻辑</p>
<ol>
<li>acquireQueued 会在一个死循环中不断尝试获得锁，失败后进入 park 阻塞 </li>
<li>如果自己是紧邻着 head（排第二位），那么再次 tryAcquire 尝试获取锁，当然这时 state 仍为 1，失败</li>
<li> 进入 shouldParkAfterFailedAcquire 逻辑，将前驱 node，即 head 的 waitStatus 改为 -1，这次返回 false（-1表示它有责任唤醒后面的节点）</li>
</ol>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220417182113673.png" alt="image-20220417182113673"></p>
<ol start="4">
<li>shouldParkAfterFailedAcquire 执行完毕回到 acquireQueued ，再次 tryAcquire 尝试获取锁，当然这时 state 仍为 1，失败 </li>
<li>当再次进入 shouldParkAfterFailedAcquire 时，这时因为其前驱 node 的 waitStatus 已经是 -1，这次返回 true </li>
<li>进入 parkAndCheckInterrupt， Thread-1 park（灰色表示）</li>
</ol>
<p>再次有多个线程经历上述过程竞争失败，变成这个样子</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220417182208894.png" alt="image-20220417182208894"></p>
<p>Thread-0 释放锁，进入 tryRelease 流程，如果成功</p>
<ul>
<li>设置 exclusiveOwnerThread 为 null </li>
<li>state = 0</li>
</ul>
<p>当前队列不为 null，并且 head 的 waitStatus = -1，进入 unparkSuccessor 流程</p>
<p>找到队列中离 head 最近的一个 Node（没取消的），unpark 恢复其运行，本例中即为 Thread-1 </p>
<p>回到 Thread-1 的 acquireQueued 流程</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220417182414703.png" alt="image-20220417182414703"></p>
<p>如果加锁成功（没有竞争），会设置</p>
<ul>
<li>exclusiveOwnerThread 为 Thread-1，state = 1 </li>
<li>head 指向刚刚 Thread-1 所在的 Node，该 Node 清空 Thread </li>
<li>原本的 head 因为从链表断开，而可被垃圾回收 </li>
</ul>
<p>如果这时候有其它线程来竞争（非公平的体现），例如这时有 Thread-4 来了</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220417182547570.png" alt="image-20220417182547570"></p>
<p>如果不巧又被 Thread-4 占了先</p>
<ul>
<li><p>Thread-4 被设置为 exclusiveOwnerThread，state = 1 </p>
</li>
<li><p>Thread-1 再次进入 acquireQueued 流程，获取锁失败，重新进入 park 阻塞</p>
</li>
</ul>
<h5 id="可重入原理"><a href="#可重入原理" class="headerlink" title="可重入原理"></a>可重入原理</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">NonfairSync</span> <span class="keyword">extends</span> <span class="title class_">Sync</span> &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// Sync 继承过来的方法, 方便阅读, 放在此处</span></span><br><span class="line">    <span class="keyword">final</span> <span class="type">boolean</span> <span class="title function_">nonfairTryAcquire</span><span class="params">(<span class="type">int</span> acquires)</span> &#123;</span><br><span class="line">        <span class="keyword">final</span> <span class="type">Thread</span> <span class="variable">current</span> <span class="operator">=</span> Thread.currentThread();</span><br><span class="line">        <span class="type">int</span> <span class="variable">c</span> <span class="operator">=</span> getState();</span><br><span class="line">        <span class="keyword">if</span> (c == <span class="number">0</span>) &#123;<span class="comment">//首次获得锁</span></span><br><span class="line">            <span class="keyword">if</span> (compareAndSetState(<span class="number">0</span>, acquires)) &#123;</span><br><span class="line">                setExclusiveOwnerThread(current);</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 如果已经获得了锁, 线程还是当前线程, 表示发生了锁重入</span></span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (current == getExclusiveOwnerThread()) &#123;</span><br><span class="line">            <span class="comment">// 关键是state++</span></span><br><span class="line">            <span class="type">int</span> <span class="variable">nextc</span> <span class="operator">=</span> c + acquires;</span><br><span class="line">            <span class="keyword">if</span> (nextc &lt; <span class="number">0</span>) <span class="comment">// overflow</span></span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">&quot;Maximum lock count exceeded&quot;</span>);</span><br><span class="line">            setState(nextc);</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Sync 继承过来的方法, 方便阅读, 放在此处</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">final</span> <span class="type">boolean</span> <span class="title function_">tryRelease</span><span class="params">(<span class="type">int</span> releases)</span> &#123;</span><br><span class="line">        <span class="comment">// state-- </span></span><br><span class="line">        <span class="type">int</span> <span class="variable">c</span> <span class="operator">=</span> getState() - releases;</span><br><span class="line">        <span class="keyword">if</span> (Thread.currentThread() != getExclusiveOwnerThread())</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalMonitorStateException</span>();</span><br><span class="line">        <span class="type">boolean</span> <span class="variable">free</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line">        <span class="comment">// 支持锁重入, 只有 state 减为 0, 才释放成功</span></span><br><span class="line">        <span class="keyword">if</span> (c == <span class="number">0</span>) &#123;</span><br><span class="line">            free = <span class="literal">true</span>;</span><br><span class="line">            setExclusiveOwnerThread(<span class="literal">null</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        setState(c);</span><br><span class="line">        <span class="keyword">return</span> free;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="可打断原理"><a href="#可打断原理" class="headerlink" title="可打断原理"></a>可打断原理</h5><p>不可打断模式</p>
<p>在此模式下，即使它被打断，仍会驻留在 AQS 队列中，一直要等到获得锁后方能得知自己被打断了，重新产生一次中断</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// Sync 继承自 AQS</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">NonfairSync</span> <span class="keyword">extends</span> <span class="title class_">Sync</span> &#123;</span><br><span class="line">    <span class="comment">// ...</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">boolean</span> <span class="title function_">parkAndCheckInterrupt</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 如果打断标记已经是 true, 则 park 会失效</span></span><br><span class="line">        LockSupport.park(<span class="built_in">this</span>);</span><br><span class="line">        <span class="comment">// interrupted 会清除打断标记,返回true</span></span><br><span class="line">        <span class="keyword">return</span> Thread.interrupted();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">final</span> <span class="type">boolean</span> <span class="title function_">acquireQueued</span><span class="params">(<span class="keyword">final</span> Node node, <span class="type">int</span> arg)</span> &#123;</span><br><span class="line">        <span class="type">boolean</span> <span class="variable">failed</span> <span class="operator">=</span> <span class="literal">true</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">boolean</span> <span class="variable">interrupted</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line">            <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">                <span class="keyword">final</span> <span class="type">Node</span> <span class="variable">p</span> <span class="operator">=</span> node.predecessor();</span><br><span class="line">                <span class="keyword">if</span> (p == head &amp;&amp; tryAcquire(arg)) &#123;</span><br><span class="line">                    setHead(node);</span><br><span class="line">                    p.next = <span class="literal">null</span>;</span><br><span class="line">                    failed = <span class="literal">false</span>;</span><br><span class="line">                    <span class="comment">// 还是需要获得锁后, 才能返回打断状态</span></span><br><span class="line">                    <span class="keyword">return</span> interrupted;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (</span><br><span class="line">                    shouldParkAfterFailedAcquire(p, node) &amp;&amp;</span><br><span class="line">                    parkAndCheckInterrupt()</span><br><span class="line">                ) &#123;</span><br><span class="line">                    <span class="comment">// 如果是因为 interrupt 被唤醒, 返回打断状态为 true</span></span><br><span class="line">                    interrupted = <span class="literal">true</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (failed)</span><br><span class="line">                cancelAcquire(node);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title function_">acquire</span><span class="params">(<span class="type">int</span> arg)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (</span><br><span class="line">            !tryAcquire(arg) &amp;&amp;</span><br><span class="line">            acquireQueued(addWaiter(Node.EXCLUSIVE), arg)</span><br><span class="line">        ) &#123;</span><br><span class="line">            <span class="comment">// 如果打断状态为 true</span></span><br><span class="line">            selfInterrupt();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">selfInterrupt</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 重新产生一次中断</span></span><br><span class="line">        Thread.currentThread().interrupt();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>可打断模式</p>
<p>直接抛出异常，不会在AQS队列等待</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">NonfairSync</span> <span class="keyword">extends</span> <span class="title class_">Sync</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title function_">acquireInterruptibly</span><span class="params">(<span class="type">int</span> arg)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        <span class="keyword">if</span> (Thread.interrupted())</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">InterruptedException</span>();</span><br><span class="line">        <span class="comment">// 如果没有获得到锁, 进入 ㈠</span></span><br><span class="line">        <span class="keyword">if</span> (!tryAcquire(arg))</span><br><span class="line">            doAcquireInterruptibly(arg);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ㈠ 可打断的获取锁流程</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">doAcquireInterruptibly</span><span class="params">(<span class="type">int</span> arg)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        <span class="keyword">final</span> <span class="type">Node</span> <span class="variable">node</span> <span class="operator">=</span> addWaiter(Node.EXCLUSIVE);</span><br><span class="line">        <span class="type">boolean</span> <span class="variable">failed</span> <span class="operator">=</span> <span class="literal">true</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">                <span class="keyword">final</span> <span class="type">Node</span> <span class="variable">p</span> <span class="operator">=</span> node.predecessor();</span><br><span class="line">                <span class="keyword">if</span> (p == head &amp;&amp; tryAcquire(arg)) &#123;</span><br><span class="line">                    setHead(node);</span><br><span class="line">                    p.next = <span class="literal">null</span>; <span class="comment">// help GC</span></span><br><span class="line">                    failed = <span class="literal">false</span>;</span><br><span class="line">                    <span class="keyword">return</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (shouldParkAfterFailedAcquire(p, node) &amp;&amp;</span><br><span class="line">                    parkAndCheckInterrupt()) &#123;</span><br><span class="line">                    <span class="comment">// 在 park 过程中如果被 interrupt 会进入此</span></span><br><span class="line">                    <span class="comment">// 这时候抛出异常, 而不会再次进入 for (;;)</span></span><br><span class="line">                    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">InterruptedException</span>();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (failed)</span><br><span class="line">                cancelAcquire(node);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="公平锁实现原理"><a href="#公平锁实现原理" class="headerlink" title="公平锁实现原理"></a>公平锁实现原理</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">FairSync</span> <span class="keyword">extends</span> <span class="title class_">Sync</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">long</span> <span class="variable">serialVersionUID</span> <span class="operator">=</span> -<span class="number">3000897897090466540L</span>;</span><br><span class="line">    <span class="keyword">final</span> <span class="keyword">void</span> <span class="title function_">lock</span><span class="params">()</span> &#123;</span><br><span class="line">        acquire(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// AQS 继承过来的方法, 方便阅读, 放在此处</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title function_">acquire</span><span class="params">(<span class="type">int</span> arg)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (</span><br><span class="line">            !tryAcquire(arg) &amp;&amp;</span><br><span class="line">            acquireQueued(addWaiter(Node.EXCLUSIVE), arg)</span><br><span class="line">        ) &#123;</span><br><span class="line">            selfInterrupt();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 与非公平锁主要区别在于 tryAcquire 方法的实现</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">final</span> <span class="type">boolean</span> <span class="title function_">tryAcquire</span><span class="params">(<span class="type">int</span> acquires)</span> &#123;</span><br><span class="line">        <span class="keyword">final</span> <span class="type">Thread</span> <span class="variable">current</span> <span class="operator">=</span> Thread.currentThread();</span><br><span class="line">        <span class="type">int</span> <span class="variable">c</span> <span class="operator">=</span> getState();</span><br><span class="line">        <span class="keyword">if</span> (c == <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="comment">// 先检查 AQS 队列中是否有前驱节点, 有就不去竞争了</span></span><br><span class="line">            <span class="keyword">if</span> (!hasQueuedPredecessors() &amp;&amp;</span><br><span class="line">                compareAndSetState(<span class="number">0</span>, acquires)) &#123;</span><br><span class="line">                setExclusiveOwnerThread(current);</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (current == getExclusiveOwnerThread()) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">nextc</span> <span class="operator">=</span> c + acquires;</span><br><span class="line">            <span class="keyword">if</span> (nextc &lt; <span class="number">0</span>)</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">&quot;Maximum lock count exceeded&quot;</span>);</span><br><span class="line">            setState(nextc);</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ㈠ AQS 继承过来的方法, 方便阅读, 放在此处</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> <span class="type">boolean</span> <span class="title function_">hasQueuedPredecessors</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">t</span> <span class="operator">=</span> tail;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">h</span> <span class="operator">=</span> head;</span><br><span class="line">        Node s;</span><br><span class="line">        <span class="comment">// h != t 时表示队列中有 Node</span></span><br><span class="line">        <span class="keyword">return</span> h != t &amp;&amp;</span><br><span class="line">            (</span><br><span class="line">            <span class="comment">// (s = h.next) == null 表示队列中还有没有老二</span></span><br><span class="line">            (s = h.next) == <span class="literal">null</span> ||</span><br><span class="line">            <span class="comment">// 或者队列中老二线程不是此线程</span></span><br><span class="line">            s.thread != Thread.currentThread()</span><br><span class="line">        );</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="条件变量实现原理"><a href="#条件变量实现原理" class="headerlink" title="条件变量实现原理"></a>条件变量实现原理</h5><p>每个条件变量其实就对应着一个等待队列，其实现类是 ConditionObject</p>
<p><strong>await 流程</strong></p>
<p>开始 Thread-0 持有锁，调用 await，进入 ConditionObject 的 addConditionWaiter 流程</p>
<p>创建新的 Node 状态为 -2（Node.CONDITION），关联 Thread-0，加入等待队列尾部</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220418094529627.png" alt="image-20220418094529627"></p>
<p>接下来进入 AQS 的 fullyRelease 流程，释放同步器上的锁</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220418094549352.png" alt="image-20220418094549352"></p>
<p>unpark AQS 队列中的下一个节点，竞争锁，假设没有其他竞争线程，那么 Thread-1 竞争成功</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220418094645154.png" alt="image-20220418094645154"></p>
<p>park 阻塞 Thread-0</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220418094702592.png" alt="image-20220418094702592"></p>
<p><strong>signal 流程</strong></p>
<p>假设 Thread-1 要来唤醒 Thread-0</p>
<p>进入 ConditionObject 的 doSignal 流程，取得等待队列中第一个 Node，即 Thread-0 所在 Node</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220418094837176.png" alt="image-20220418094837176"></p>
<p>执行 transferForSignal 流程，将该 Node 加入 AQS 队列尾部，将 Thread-0 的 waitStatus 改为 0，Thread-3 的 waitStatus 改为 -1</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220418094912468.png" alt="image-20220418094912468"></p>
<p>Thread-1 释放锁，进入 unlock 流程，略</p>
<h4 id="读写锁"><a href="#读写锁" class="headerlink" title="读写锁"></a>读写锁</h4><h5 id="ReentrantReadWriteLock"><a href="#ReentrantReadWriteLock" class="headerlink" title="ReentrantReadWriteLock"></a>ReentrantReadWriteLock</h5><p>当读操作远远高于写操作时，这时候使用读写锁 让 读-读可以并发，提高性能。 类似于数据库中的 select … from … lock in share mode</p>
<p>提供一个 数据容器类内部分别使用读锁保护数据的 read() 方法，写锁保护数据的 write() 方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">DataContainer</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> Object data;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">ReentrantReadWriteLock</span> <span class="variable">rw</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReentrantReadWriteLock</span>();</span><br><span class="line">    <span class="keyword">private</span> ReentrantReadWriteLock.<span class="type">ReadLock</span> <span class="variable">r</span> <span class="operator">=</span> rw.readLock();</span><br><span class="line">    <span class="keyword">private</span> ReentrantReadWriteLock.<span class="type">WriteLock</span> <span class="variable">w</span> <span class="operator">=</span> rw.writeLock();</span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">read</span><span class="params">()</span> &#123;</span><br><span class="line">        log.debug(<span class="string">&quot;获取读锁...&quot;</span>);</span><br><span class="line">        r.lock();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            log.debug(<span class="string">&quot;读取&quot;</span>);</span><br><span class="line">            sleep(<span class="number">1</span>);</span><br><span class="line">            <span class="keyword">return</span> data;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            log.debug(<span class="string">&quot;释放读锁...&quot;</span>);</span><br><span class="line">            r.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">write</span><span class="params">()</span> &#123;</span><br><span class="line">        log.debug(<span class="string">&quot;获取写锁...&quot;</span>);</span><br><span class="line">        w.lock();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            log.debug(<span class="string">&quot;写入&quot;</span>);</span><br><span class="line">            sleep(<span class="number">1</span>);</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            log.debug(<span class="string">&quot;释放写锁...&quot;</span>);</span><br><span class="line">            w.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>读锁-读锁 可以并发；读锁-写锁 相互阻塞；写锁-写锁 也是相互阻塞的</p>
<blockquote>
<p>注意事项</p>
<ul>
<li>读锁不支持条件变量</li>
<li>重入时升级不支持：即持有读锁的情况下去获取写锁，会阻塞</li>
<li>重入时降级支持：即持有写锁的情况下可以获取读锁</li>
</ul>
</blockquote>
<h5 id="应用之缓存"><a href="#应用之缓存" class="headerlink" title="应用之缓存"></a>应用之缓存</h5><p>读写锁实现一致性缓存</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">GenericCachedDao</span>&lt;T&gt; &#123;</span><br><span class="line">    <span class="comment">// HashMap 作为缓存非线程安全, 需要保护</span></span><br><span class="line">    HashMap&lt;SqlPair, T&gt; map = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="type">ReentrantReadWriteLock</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReentrantReadWriteLock</span>(); </span><br><span class="line">    <span class="type">GenericDao</span> <span class="variable">genericDao</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">GenericDao</span>();</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">update</span><span class="params">(String sql, Object... params)</span> &#123;</span><br><span class="line">        <span class="type">SqlPair</span> <span class="variable">key</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SqlPair</span>(sql, params);</span><br><span class="line">        <span class="comment">// 加写锁, 防止其它线程对缓存读取和更改</span></span><br><span class="line">        lock.writeLock().lock();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">rows</span> <span class="operator">=</span> genericDao.update(sql, params);</span><br><span class="line">            map.clear();</span><br><span class="line">            <span class="keyword">return</span> rows;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            lock.writeLock().unlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> T <span class="title function_">queryOne</span><span class="params">(Class&lt;T&gt; beanClass, String sql, Object... params)</span> &#123;</span><br><span class="line">        <span class="type">SqlPair</span> <span class="variable">key</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SqlPair</span>(sql, params);</span><br><span class="line">        <span class="comment">// 加读锁, 防止其它线程对缓存更改</span></span><br><span class="line">        lock.readLock().lock();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">T</span> <span class="variable">value</span> <span class="operator">=</span> map.get(key);</span><br><span class="line">            <span class="keyword">if</span> (value != <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="keyword">return</span> value;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            lock.readLock().unlock();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 加写锁, 防止其它线程对缓存读取和更改</span></span><br><span class="line">        lock.writeLock().lock();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// get 方法上面部分是可能多个线程进来的, 可能已经向缓存填充了数据</span></span><br><span class="line">            <span class="comment">// 为防止重复查询数据库, 再次验证</span></span><br><span class="line">            <span class="type">T</span> <span class="variable">value</span> <span class="operator">=</span> map.get(key);</span><br><span class="line">            <span class="keyword">if</span> (value == <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="comment">// 如果没有, 查询数据库</span></span><br><span class="line">                value = genericDao.queryOne(beanClass, sql, params);</span><br><span class="line">                map.put(key, value);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> value;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            lock.writeLock().unlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 作为 key 保证其是不可变的</span></span><br><span class="line">    <span class="keyword">class</span> <span class="title class_">SqlPair</span> &#123;</span><br><span class="line">        <span class="keyword">private</span> String sql;</span><br><span class="line">        <span class="keyword">private</span> Object[] params;</span><br><span class="line">        <span class="keyword">public</span> <span class="title function_">SqlPair</span><span class="params">(String sql, Object[] params)</span> &#123;</span><br><span class="line">            <span class="built_in">this</span>.sql = sql;</span><br><span class="line">            <span class="built_in">this</span>.params = params;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">equals</span><span class="params">(Object o)</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (<span class="built_in">this</span> == o) &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (o == <span class="literal">null</span> || getClass() != o.getClass()) &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="type">SqlPair</span> <span class="variable">sqlPair</span> <span class="operator">=</span> (SqlPair) o;</span><br><span class="line">            <span class="keyword">return</span> sql.equals(sqlPair.sql) &amp;&amp;</span><br><span class="line">                Arrays.equals(params, sqlPair.params);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">hashCode</span><span class="params">()</span> &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">result</span> <span class="operator">=</span> Objects.hash(sql);</span><br><span class="line">            result = <span class="number">31</span> * result + Arrays.hashCode(params);</span><br><span class="line">            <span class="keyword">return</span> result;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>注意：</p>
<ul>
<li><p>以上实现体现的是读写锁的应用，保证缓存和数据库的一致性，但有下面的问题没有考虑</p>
</li>
<li><p>适合读多写少，如果写操作比较频繁，以上实现性能低</p>
</li>
<li><p>没有考虑缓存容量</p>
</li>
<li><p>没有考虑缓存过期</p>
</li>
<li><p>只适合单机</p>
</li>
<li><p>并发性还是低，目前只会用一把锁</p>
</li>
<li><p>更新方法太过简单粗暴，清空了所有 key（考虑按类型分区或重新设计 key）</p>
</li>
<li><p>乐观锁实现：用 CAS 去更新</p>
</li>
</ul>
</blockquote>
<h5 id="读写锁原理"><a href="#读写锁原理" class="headerlink" title="读写锁原理"></a>读写锁原理</h5><p>读写锁用的是同一个 Sycn 同步器，因此等待队列、state 等也是同一个</p>
<p><strong>t1 w.lock，t2 r.lock</strong></p>
<p>1） t1 成功上锁，流程与 ReentrantLock 加锁相比没有特殊之处，不同是写锁状态占了 state 的低 16 位，而读锁使用的是 state 的高 16 位</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220418153312998.png" alt="image-20220418153312998"></p>
<p>2）t2 执行 r.lock，这时进入读锁的 sync.acquireShared(1) 流程，首先会进入 tryAcquireShared 流程。如果有写锁占据，那么 tryAcquireShared 返回 -1 表示失败</p>
<blockquote>
<p>tryAcquireShared 返回值表示</p>
<ul>
<li>-1 表示失败</li>
<li>0 表示成功，但后继节点不会继续唤醒</li>
<li>正数表示成功，而且数值是还有几个后继节点需要唤醒，读写锁返回 1</li>
</ul>
</blockquote>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220418154455649.png" alt="image-20220418154455649"></p>
<p>3）这时会进入 sync.doAcquireShared(1) 流程，首先也是调用 addWaiter 添加节点，不同之处在于节点被设置为 Node.SHARED 模式而非 Node.EXCLUSIVE 模式，注意此时 t2 仍处于活跃状态</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220418154521364.png" alt="image-20220418154521364"></p>
<p>4）t2 会看看自己的节点是不是老二，如果是，还会再次调用 tryAcquireShared(1) 来尝试获取锁</p>
<p>5）如果没有成功，在 doAcquireShared 内 for (;;) 循环一次，把前驱节点的 waitStatus 改为 -1，再 for (;;) 循环一 次尝试 tryAcquireShared(1) 如果还不成功，那么在 parkAndCheckInterrupt() 处 park</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220418154547256.png" alt="image-20220418154547256"></p>
<p><strong>t3 r.lock，t4 w.lock</strong></p>
<p>这种状态下，假设又有 t3 加读锁和 t4 加写锁，这期间 t1 仍然持有锁，就变成了下面的样子</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220418154652111.png" alt="image-20220418154652111"></p>
<p>读锁和写锁的节点状态不同，shared Ex</p>
<p><strong>t1 w.unlock</strong></p>
<p>这时会走到写锁的 sync.release(1) 流程，调用 sync.tryRelease(1) 成功，变成下面的样子</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220418215022192.png" alt="image-20220418215022192"></p>
<p>接下来执行唤醒流程 sync.unparkSuccessor，即让老二恢复运行，这时 t2 在 doAcquireShared 内 parkAndCheckInterrupt() 处恢复运行</p>
<p>这回再来一次 for (;;) 执行 tryAcquireShared 成功则让读锁计数加一</p>
<p>这时 t2 已经恢复运行，接下来 t2 调用 setHeadAndPropagate(node, 1)，它原本所在节点被置为头节点</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220418215508562.png" alt="image-20220418215508562"></p>
<p>事情还没完，在 setHeadAndPropagate 方法内还会检查下一个节点是否是 shared，如果是则调用 doReleaseShared() 将 head 的状态从 -1 改为 0 并唤醒老二，这时 t3 在 doAcquireShared 内 parkAndCheckInterrupt() 处恢复运行<strong>（读锁-读锁并发）</strong></p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220418215629529.png" alt="image-20220418215629529"></p>
<p>这回再来一次 for (;;) 执行 tryAcquireShared 成功则让读锁计数加一</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220418215915222.png" alt="image-20220418215915222"></p>
<p>这时 t3 已经恢复运行，接下来 t3 调用 setHeadAndPropagate(node, 1)，它原本所在节点被置为头节点</p>
<p>下一个节点不是 shared 了，因此不会继续唤醒 t4 所在节点</p>
<p><strong>t2 r.unlock，t3 r.unlock</strong></p>
<p>t2 进入 sync.releaseShared(1) 中，调用 tryReleaseShared(1) 让计数减一，但由于计数还不为零</p>
<p>t3 进入 sync.releaseShared(1) 中，调用 tryReleaseShared(1) 让计数减一，这回计数为零了，进入 doReleaseShared() 将头节点从 -1 改为 0 并唤醒老二，即</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220418220917582.png" alt="image-20220418220917582"></p>
<p>之后 t4 在 acquireQueued 中 parkAndCheckInterrupt 处恢复运行，再次 for (;;) 这次自己是老二，并且没有其他 竞争，tryAcquire(1) 成功，修改头结点，流程结束</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220418220955063.png" alt="image-20220418220955063"></p>
<h5 id="源码"><a href="#源码" class="headerlink" title="源码"></a>源码</h5><p>写锁上锁流程</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">NonfairSync</span> <span class="keyword">extends</span> <span class="title class_">Sync</span> &#123;</span><br><span class="line">    <span class="comment">// ... 省略无关代码</span></span><br><span class="line">    <span class="comment">// WriteLock 方法, 方便阅读, 放在此处</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">unlock</span><span class="params">()</span> &#123;</span><br><span class="line">        sync.release(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// AQS 继承过来的方法, 方便阅读, 放在此处</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> <span class="type">boolean</span> <span class="title function_">release</span><span class="params">(<span class="type">int</span> arg)</span> &#123;</span><br><span class="line">        <span class="comment">// 尝试释放写锁成功</span></span><br><span class="line">        <span class="keyword">if</span> (tryRelease(arg)) &#123;</span><br><span class="line">            <span class="comment">// unpark AQS 中等待的线程</span></span><br><span class="line">            <span class="type">Node</span> <span class="variable">h</span> <span class="operator">=</span> head;</span><br><span class="line">            <span class="keyword">if</span> (h != <span class="literal">null</span> &amp;&amp; h.waitStatus != <span class="number">0</span>)</span><br><span class="line">                unparkSuccessor(h);</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// Sync 继承过来的方法, 方便阅读, 放在此处</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">final</span> <span class="type">boolean</span> <span class="title function_">tryRelease</span><span class="params">(<span class="type">int</span> releases)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (!isHeldExclusively())</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalMonitorStateException</span>();</span><br><span class="line">        <span class="type">int</span> <span class="variable">nextc</span> <span class="operator">=</span> getState() - releases;</span><br><span class="line">        <span class="comment">// 因为可重入的原因, 写锁计数为 0, 才算释放成功</span></span><br><span class="line">        <span class="type">boolean</span> <span class="variable">free</span> <span class="operator">=</span> exclusiveCount(nextc) == <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">if</span> (free) &#123;</span><br><span class="line">            setExclusiveOwnerThread(<span class="literal">null</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        setState(nextc);</span><br><span class="line">        <span class="keyword">return</span> free;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">int</span> <span class="variable">w</span> <span class="operator">=</span> exclusiveCount(c);</span><br><span class="line"><span class="keyword">if</span> (c != <span class="number">0</span>) &#123;</span><br><span class="line">    <span class="keyword">if</span> (</span><br><span class="line">        <span class="comment">// c != 0 and w == 0 表示有读锁, 或者</span></span><br><span class="line">        w == <span class="number">0</span> ||</span><br><span class="line">        <span class="comment">// 如果 exclusiveOwnerThread 不是自己</span></span><br><span class="line">        current != getExclusiveOwnerThread()</span><br><span class="line">    ) &#123;</span><br><span class="line">        <span class="comment">// 获得锁失败</span></span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 写锁计数超过低 16 位, 报异常</span></span><br><span class="line">    <span class="keyword">if</span> (w + exclusiveCount(acquires) &gt; MAX_COUNT)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">&quot;Maximum lock count exceeded&quot;</span>);</span><br><span class="line">    <span class="comment">// 写锁重入, 获得锁成功</span></span><br><span class="line">    setState(c + acquires);</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125; </span><br><span class="line"><span class="keyword">if</span> (</span><br><span class="line">    <span class="comment">// 判断写锁是否该阻塞, 或者</span></span><br><span class="line">    writerShouldBlock() ||</span><br><span class="line">    <span class="comment">// 尝试更改计数失败</span></span><br><span class="line">    !compareAndSetState(c, c + acquires)</span><br><span class="line">) &#123;</span><br><span class="line">    <span class="comment">// 获得锁失败</span></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 获得锁成功</span></span><br><span class="line">setExclusiveOwnerThread(current);</span><br><span class="line"><span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 非公平锁 writerShouldBlock 总是返回 false, 无需阻塞</span></span><br><span class="line"><span class="keyword">final</span> <span class="type">boolean</span> <span class="title function_">writerShouldBlock</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>写锁释放流程</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">NonfairSync</span> <span class="keyword">extends</span> <span class="title class_">Sync</span> &#123;</span><br><span class="line">    <span class="comment">// ... 省略无关代码</span></span><br><span class="line"></span><br><span class="line">    <span class="comment">// WriteLock 方法, 方便阅读, 放在此处</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">unlock</span><span class="params">()</span> &#123;</span><br><span class="line">        sync.release(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// AQS 继承过来的方法, 方便阅读, 放在此处</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> <span class="type">boolean</span> <span class="title function_">release</span><span class="params">(<span class="type">int</span> arg)</span> &#123;</span><br><span class="line">        <span class="comment">// 尝试释放写锁成功</span></span><br><span class="line">        <span class="keyword">if</span> (tryRelease(arg)) &#123;</span><br><span class="line">            <span class="comment">// unpark AQS 中等待的线程</span></span><br><span class="line">            <span class="type">Node</span> <span class="variable">h</span> <span class="operator">=</span> head;</span><br><span class="line">            <span class="keyword">if</span> (h != <span class="literal">null</span> &amp;&amp; h.waitStatus != <span class="number">0</span>)</span><br><span class="line">                unparkSuccessor(h);</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Sync 继承过来的方法, 方便阅读, 放在此处</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">final</span> <span class="type">boolean</span> <span class="title function_">tryRelease</span><span class="params">(<span class="type">int</span> releases)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (!isHeldExclusively())</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalMonitorStateException</span>();</span><br><span class="line">        <span class="type">int</span> <span class="variable">nextc</span> <span class="operator">=</span> getState() - releases;</span><br><span class="line">        <span class="comment">// 因为可重入的原因, 写锁计数为 0, 才算释放成功</span></span><br><span class="line">        <span class="type">boolean</span> <span class="variable">free</span> <span class="operator">=</span> exclusiveCount(nextc) == <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">if</span> (free) &#123;</span><br><span class="line">            setExclusiveOwnerThread(<span class="literal">null</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        setState(nextc);</span><br><span class="line">        <span class="keyword">return</span> free;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>读锁上锁流程</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">NonfairSync</span> <span class="keyword">extends</span> <span class="title class_">Sync</span> &#123;</span><br><span class="line">    <span class="comment">// ReadLock 方法, 方便阅读, 放在此处</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">lock</span><span class="params">()</span> &#123;</span><br><span class="line">        sync.acquireShared(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// AQS 继承过来的方法, 方便阅读, 放在此处</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title function_">acquireShared</span><span class="params">(<span class="type">int</span> arg)</span> &#123;</span><br><span class="line">        <span class="comment">// tryAcquireShared 返回负数, 表示获取读锁失败</span></span><br><span class="line">        <span class="keyword">if</span> (tryAcquireShared(arg) &lt; <span class="number">0</span>) &#123;</span><br><span class="line">            doAcquireShared(arg);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// Sync 继承过来的方法, 方便阅读, 放在此处</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">final</span> <span class="type">int</span> <span class="title function_">tryAcquireShared</span><span class="params">(<span class="type">int</span> unused)</span> &#123;</span><br><span class="line">        <span class="type">Thread</span> <span class="variable">current</span> <span class="operator">=</span> Thread.currentThread();</span><br><span class="line">        <span class="type">int</span> <span class="variable">c</span> <span class="operator">=</span> getState();</span><br><span class="line">        <span class="comment">// 如果是其它线程持有写锁, 获取读锁失败</span></span><br><span class="line">        <span class="keyword">if</span> ( </span><br><span class="line">            exclusiveCount(c) != <span class="number">0</span> &amp;&amp;</span><br><span class="line">            getExclusiveOwnerThread() != current</span><br><span class="line">        ) &#123;</span><br><span class="line">            <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">int</span> <span class="variable">r</span> <span class="operator">=</span> sharedCount(c);</span><br><span class="line">        <span class="keyword">if</span> (</span><br><span class="line">            <span class="comment">// 读锁不该阻塞(如果老二是写锁，读锁该阻塞), 并且</span></span><br><span class="line">            !readerShouldBlock() &amp;&amp;</span><br><span class="line">            <span class="comment">// 小于读锁计数, 并且</span></span><br><span class="line">            r &lt; MAX_COUNT &amp;&amp;</span><br><span class="line">            <span class="comment">// 尝试增加计数成功</span></span><br><span class="line">            compareAndSetState(c, c + SHARED_UNIT)</span><br><span class="line">        ) &#123;</span><br><span class="line">            <span class="comment">// ... 省略不重要的代码</span></span><br><span class="line">            <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> fullTryAcquireShared(current);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 非公平锁 readerShouldBlock 看 AQS 队列中第一个节点是否是写锁</span></span><br><span class="line">    <span class="comment">// true 则该阻塞, false 则不阻塞</span></span><br><span class="line">    <span class="keyword">final</span> <span class="type">boolean</span> <span class="title function_">readerShouldBlock</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> apparentlyFirstQueuedIsExclusive();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// AQS 继承过来的方法, 方便阅读, 放在此处</span></span><br><span class="line">    <span class="comment">// 与 tryAcquireShared 功能类似, 但会不断尝试 for (;;) 获取读锁, 执行过程中无阻塞</span></span><br><span class="line">    <span class="keyword">final</span> <span class="type">int</span> <span class="title function_">fullTryAcquireShared</span><span class="params">(Thread current)</span> &#123;</span><br><span class="line">        <span class="type">HoldCounter</span> <span class="variable">rh</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">        <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">c</span> <span class="operator">=</span> getState();</span><br><span class="line">            <span class="keyword">if</span> (exclusiveCount(c) != <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="keyword">if</span> (getExclusiveOwnerThread() != current)</span><br><span class="line">                    <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (readerShouldBlock()) &#123;</span><br><span class="line">                <span class="comment">// ... 省略不重要的代码</span></span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (sharedCount(c) == MAX_COUNT)</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">&quot;Maximum lock count exceeded&quot;</span>);</span><br><span class="line">            <span class="keyword">if</span> (compareAndSetState(c, c + SHARED_UNIT)) &#123;</span><br><span class="line">                <span class="comment">// ... 省略不重要的代码</span></span><br><span class="line">                <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// AQS 继承过来的方法, 方便阅读, 放在此处</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">doAcquireShared</span><span class="params">(<span class="type">int</span> arg)</span> &#123;</span><br><span class="line">        <span class="comment">// 将当前线程关联到一个 Node 对象上, 模式为共享模式</span></span><br><span class="line">        <span class="keyword">final</span> <span class="type">Node</span> <span class="variable">node</span> <span class="operator">=</span> addWaiter(Node.SHARED);</span><br><span class="line">        <span class="type">boolean</span> <span class="variable">failed</span> <span class="operator">=</span> <span class="literal">true</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">boolean</span> <span class="variable">interrupted</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line">            <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">                <span class="keyword">final</span> <span class="type">Node</span> <span class="variable">p</span> <span class="operator">=</span> node.predecessor();</span><br><span class="line">                <span class="keyword">if</span> (p == head) &#123;</span><br><span class="line">                    <span class="comment">// 再一次尝试获取读锁</span></span><br><span class="line">                    <span class="type">int</span> <span class="variable">r</span> <span class="operator">=</span> tryAcquireShared(arg);</span><br><span class="line">                    <span class="comment">// 成功</span></span><br><span class="line">                    <span class="keyword">if</span> (r &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">                        <span class="comment">// ㈠</span></span><br><span class="line">                        <span class="comment">// r 表示可用资源数, 在这里总是 1 允许传播</span></span><br><span class="line">                        <span class="comment">//（唤醒 AQS 中下一个 Share 节点）</span></span><br><span class="line">                        setHeadAndPropagate(node, r);</span><br><span class="line">                        p.next = <span class="literal">null</span>; <span class="comment">// help GC</span></span><br><span class="line">                        <span class="keyword">if</span> (interrupted)</span><br><span class="line">                            selfInterrupt();</span><br><span class="line">                        failed = <span class="literal">false</span>;</span><br><span class="line">                        <span class="keyword">return</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (</span><br><span class="line">                    <span class="comment">// 是否在获取读锁失败时阻塞（前一个阶段 waitStatus == Node.SIGNAL）</span></span><br><span class="line">                    shouldParkAfterFailedAcquire(p, node) &amp;&amp;</span><br><span class="line">                    <span class="comment">// park 当前线程</span></span><br><span class="line">                    parkAndCheckInterrupt()</span><br><span class="line">                ) &#123;</span><br><span class="line">                    interrupted = <span class="literal">true</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (failed)</span><br><span class="line">                cancelAcquire(node);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ㈠ AQS 继承过来的方法, 方便阅读, 放在此处</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">setHeadAndPropagate</span><span class="params">(Node node, <span class="type">int</span> propagate)</span> &#123;</span><br><span class="line">        <span class="type">Node</span> <span class="variable">h</span> <span class="operator">=</span> head; <span class="comment">// Record old head for check below</span></span><br><span class="line">        <span class="comment">// 设置自己为 head</span></span><br><span class="line">        setHead(node);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// propagate 表示有共享资源（例如共享读锁或信号量）</span></span><br><span class="line">        <span class="comment">// 原 head waitStatus == Node.SIGNAL 或 Node.PROPAGATE</span></span><br><span class="line">        <span class="comment">// 现在 head waitStatus == Node.SIGNAL 或 Node.PROPAGATE</span></span><br><span class="line">        <span class="keyword">if</span> (propagate &gt; <span class="number">0</span> || h == <span class="literal">null</span> || h.waitStatus &lt; <span class="number">0</span> ||</span><br><span class="line">            (h = head) == <span class="literal">null</span> || h.waitStatus &lt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="type">Node</span> <span class="variable">s</span> <span class="operator">=</span> node.next;</span><br><span class="line">            <span class="comment">// 如果是最后一个节点或者是等待共享读锁的节点</span></span><br><span class="line">            <span class="keyword">if</span> (s == <span class="literal">null</span> || s.isShared()) &#123;</span><br><span class="line">                <span class="comment">// 进入 ㈡</span></span><br><span class="line">                doReleaseShared();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// ㈡ AQS 继承过来的方法, 方便阅读, 放在此处</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">doReleaseShared</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 如果 head.waitStatus == Node.SIGNAL ==&gt; 0 成功, 下一个节点 unpark</span></span><br><span class="line">        <span class="comment">// 如果 head.waitStatus == 0 ==&gt; Node.PROPAGATE, 为了解决 bug, 见后面分析</span></span><br><span class="line">        <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">            <span class="type">Node</span> <span class="variable">h</span> <span class="operator">=</span> head;</span><br><span class="line">            <span class="comment">// 队列还有节点</span></span><br><span class="line">            <span class="keyword">if</span> (h != <span class="literal">null</span> &amp;&amp; h != tail) &#123;</span><br><span class="line">                <span class="type">int</span> <span class="variable">ws</span> <span class="operator">=</span> h.waitStatus;</span><br><span class="line">                <span class="keyword">if</span> (ws == Node.SIGNAL) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (!compareAndSetWaitStatus(h, Node.SIGNAL, <span class="number">0</span>))</span><br><span class="line">                        <span class="keyword">continue</span>; <span class="comment">// loop to recheck cases</span></span><br><span class="line">                    <span class="comment">// 下一个节点 unpark 如果成功获取读锁</span></span><br><span class="line">                    <span class="comment">// 并且下下个节点还是 shared, 继续 doReleaseShared</span></span><br><span class="line">                    unparkSuccessor(h);</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">else</span> <span class="keyword">if</span> (ws == <span class="number">0</span> &amp;&amp;</span><br><span class="line">                         !compareAndSetWaitStatus(h, <span class="number">0</span>, Node.PROPAGATE))</span><br><span class="line">                    <span class="keyword">continue</span>; <span class="comment">// loop on failed CAS</span></span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (h == head) <span class="comment">// loop if head changed</span></span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;    </span><br></pre></td></tr></table></figure>

<p>读锁释放流程</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">NonfairSync</span> <span class="keyword">extends</span> <span class="title class_">Sync</span> &#123;</span><br><span class="line">    <span class="comment">// ReadLock 方法, 方便阅读, 放在此处</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">unlock</span><span class="params">()</span> &#123;</span><br><span class="line">        sync.releaseShared(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// AQS 继承过来的方法, 方便阅读, 放在此处</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> <span class="type">boolean</span> <span class="title function_">releaseShared</span><span class="params">(<span class="type">int</span> arg)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (tryReleaseShared(arg)) &#123;</span><br><span class="line">            doReleaseShared();</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// Sync 继承过来的方法, 方便阅读, 放在此处</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">final</span> <span class="type">boolean</span> <span class="title function_">tryReleaseShared</span><span class="params">(<span class="type">int</span> unused)</span> &#123;</span><br><span class="line">        <span class="comment">// ... 省略不重要的代码</span></span><br><span class="line">        <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">c</span> <span class="operator">=</span> getState();</span><br><span class="line">            <span class="type">int</span> <span class="variable">nextc</span> <span class="operator">=</span> c - SHARED_UNIT;</span><br><span class="line">            <span class="keyword">if</span> (compareAndSetState(c, nextc)) &#123;</span><br><span class="line">                <span class="comment">// 读锁的计数不会影响其它获取读锁线程, 但会影响其它获取写锁线程</span></span><br><span class="line">                <span class="comment">// 计数为 0 才是真正释放</span></span><br><span class="line">                <span class="type">return</span> <span class="variable">nextc</span> <span class="operator">=</span>= <span class="number">0</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// AQS 继承过来的方法, 方便阅读, 放在此处</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">doReleaseShared</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 如果 head.waitStatus == Node.SIGNAL ==&gt; 0 成功, 下一个节点 unpark</span></span><br><span class="line">        <span class="comment">// 如果 head.waitStatus == 0 ==&gt; Node.PROPAGATE </span></span><br><span class="line">        <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">            <span class="type">Node</span> <span class="variable">h</span> <span class="operator">=</span> head;</span><br><span class="line">            <span class="keyword">if</span> (h != <span class="literal">null</span> &amp;&amp; h != tail) &#123;</span><br><span class="line">                <span class="type">int</span> <span class="variable">ws</span> <span class="operator">=</span> h.waitStatus;</span><br><span class="line">                <span class="comment">// 如果有其它线程也在释放读锁，那么需要将 waitStatus 先改为 0</span></span><br><span class="line">                <span class="comment">// 防止 unparkSuccessor 被多次执行</span></span><br><span class="line">                <span class="keyword">if</span> (ws == Node.SIGNAL) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (!compareAndSetWaitStatus(h, Node.SIGNAL, <span class="number">0</span>))</span><br><span class="line">                        <span class="keyword">continue</span>; <span class="comment">// loop to recheck cases</span></span><br><span class="line">                    unparkSuccessor(h);</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="comment">// 如果已经是 0 了，改为 -3，用来解决传播性，见后文信号量 bug 分析</span></span><br><span class="line">                <span class="keyword">else</span> <span class="keyword">if</span> (ws == <span class="number">0</span> &amp;&amp;</span><br><span class="line">                         !compareAndSetWaitStatus(h, <span class="number">0</span>, Node.PROPAGATE))</span><br><span class="line">                    <span class="keyword">continue</span>; <span class="comment">// loop on failed CAS</span></span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (h == head) <span class="comment">// loop if head changed</span></span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="StampedLock"><a href="#StampedLock" class="headerlink" title="StampedLock"></a>StampedLock</h5><p>该类自 JDK 8 加入，是为了进一步优化读性能，它的特点是在使用读锁、写锁时都必须配合【戳】使用</p>
<p>加解读锁</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">long</span> <span class="variable">stamp</span> <span class="operator">=</span> lock.readLock();</span><br><span class="line">lock.unlockRead(stamp);</span><br></pre></td></tr></table></figure>

<p>加解写锁</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">long</span> <span class="variable">stamp</span> <span class="operator">=</span> lock.writeLock();</span><br><span class="line">lock.unlockWrite(stamp);</span><br></pre></td></tr></table></figure>

<p>乐观读，StampedLock 支持 tryOptimisticRead() 方法（乐观读），读取完毕后需要做一次 戳校验，如果校验通过，表示这期间确实没有写操作，数据可以安全使用，如果校验没通过，需要重新获取读锁，保证数据安全。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//返回戳</span></span><br><span class="line"><span class="type">long</span> <span class="variable">stamp</span> <span class="operator">=</span> lock.tryOptimisticRead();</span><br><span class="line"><span class="comment">// 验戳</span></span><br><span class="line"><span class="keyword">if</span>(!lock.validate(stamp))&#123;</span><br><span class="line"> <span class="comment">// 锁升级</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>提供一个 数据容器类 内部分别使用读锁保护数据的 read() 方法，写锁保护数据的 write() 方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">DataContainerStamped</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> data;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">StampedLock</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">StampedLock</span>();</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">DataContainerStamped</span><span class="params">(<span class="type">int</span> data)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.data = data;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//readTime是为了测试加的参数</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">read</span><span class="params">(<span class="type">int</span> readTime)</span> &#123;</span><br><span class="line">        <span class="type">long</span> <span class="variable">stamp</span> <span class="operator">=</span> lock.tryOptimisticRead();</span><br><span class="line">        log.debug(<span class="string">&quot;optimistic read locking...&#123;&#125;&quot;</span>, stamp);</span><br><span class="line">        sleep(readTime);</span><br><span class="line">        <span class="keyword">if</span> (lock.validate(stamp)) &#123;</span><br><span class="line">            log.debug(<span class="string">&quot;read finish...&#123;&#125;, data:&#123;&#125;&quot;</span>, stamp, data);</span><br><span class="line">            <span class="keyword">return</span> data;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 锁升级 - 读锁</span></span><br><span class="line">        log.debug(<span class="string">&quot;updating to read lock... &#123;&#125;&quot;</span>, stamp);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            stamp = lock.readLock();</span><br><span class="line">            log.debug(<span class="string">&quot;read lock &#123;&#125;&quot;</span>, stamp);</span><br><span class="line">            sleep(readTime);</span><br><span class="line">            log.debug(<span class="string">&quot;read finish...&#123;&#125;, data:&#123;&#125;&quot;</span>, stamp, data);</span><br><span class="line">            <span class="keyword">return</span> data;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            log.debug(<span class="string">&quot;read unlock &#123;&#125;&quot;</span>, stamp);</span><br><span class="line">            lock.unlockRead(stamp);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">write</span><span class="params">(<span class="type">int</span> newData)</span> &#123;</span><br><span class="line">        <span class="type">long</span> <span class="variable">stamp</span> <span class="operator">=</span> lock.writeLock();</span><br><span class="line">        log.debug(<span class="string">&quot;write lock &#123;&#125;&quot;</span>, stamp);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            sleep(<span class="number">2</span>);</span><br><span class="line">            <span class="built_in">this</span>.data = newData;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            log.debug(<span class="string">&quot;write unlock &#123;&#125;&quot;</span>, stamp);</span><br><span class="line">            lock.unlockWrite(stamp);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>注意 </p>
<ul>
<li>StampedLock 不支持条件变量</li>
<li>StampedLock 不支持可重入</li>
</ul>
</blockquote>
<h4 id="Semaphore"><a href="#Semaphore" class="headerlink" title="Semaphore"></a>Semaphore</h4><p>信号量，用来限制能同时访问共享资源的线程上限。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="comment">// 1. 创建 semaphore 对象</span></span><br><span class="line">    <span class="type">Semaphore</span> <span class="variable">semaphore</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Semaphore</span>(<span class="number">3</span>);</span><br><span class="line">    <span class="comment">// 2. 10个线程同时运行</span></span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) &#123;</span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">            <span class="comment">// 3. 获取许可</span></span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                semaphore.acquire();</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                log.debug(<span class="string">&quot;running...&quot;</span>);</span><br><span class="line">                sleep(<span class="number">1</span>);</span><br><span class="line">                log.debug(<span class="string">&quot;end...&quot;</span>);</span><br><span class="line">            &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                <span class="comment">// 4. 释放许可</span></span><br><span class="line">                semaphore.release();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;).start();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="应用—限制对共享资源的使用"><a href="#应用—限制对共享资源的使用" class="headerlink" title="应用—限制对共享资源的使用"></a>应用—限制对共享资源的使用</h5><ul>
<li>使用 Semaphore 限流，在访问高峰期时，让请求线程阻塞，高峰期过去再释放许可，当然它只适合限制单机 线程数量，并且仅是限制线程数，而不是限制资源数（例如连接数，请对比 Tomcat LimitLatch 的实现）</li>
<li>用 Semaphore 实现简单连接池，对比『享元模式』下的实现（用wait notify），性能和可读性显然更好， 注意下面的实现中线程数和数据库连接数是相等的</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Pool</span> &#123;</span><br><span class="line">    <span class="comment">// 1. 连接池大小</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">int</span> poolSize;</span><br><span class="line">    <span class="comment">// 2. 连接对象数组</span></span><br><span class="line">    <span class="keyword">private</span> Connection[] connections;</span><br><span class="line">    <span class="comment">// 3. 连接状态数组 0 表示空闲， 1 表示繁忙</span></span><br><span class="line">    <span class="keyword">private</span> AtomicIntegerArray states;</span><br><span class="line">    <span class="keyword">private</span> Semaphore semaphore;</span><br><span class="line">    <span class="comment">// 4. 构造方法初始化</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Pool</span><span class="params">(<span class="type">int</span> poolSize)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.poolSize = poolSize;</span><br><span class="line">        <span class="comment">// 让许可数与资源数一致</span></span><br><span class="line">        <span class="built_in">this</span>.semaphore = <span class="keyword">new</span> <span class="title class_">Semaphore</span>(poolSize);</span><br><span class="line">        <span class="built_in">this</span>.connections = <span class="keyword">new</span> <span class="title class_">Connection</span>[poolSize];</span><br><span class="line">        <span class="built_in">this</span>.states = <span class="keyword">new</span> <span class="title class_">AtomicIntegerArray</span>(<span class="keyword">new</span> <span class="title class_">int</span>[poolSize]);</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; poolSize; i++) &#123;</span><br><span class="line">            connections[i] = <span class="keyword">new</span> <span class="title class_">MockConnection</span>(<span class="string">&quot;连接&quot;</span> + (i+<span class="number">1</span>));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 5. 借连接</span></span><br><span class="line">    <span class="keyword">public</span> Connection <span class="title function_">borrow</span><span class="params">()</span> &#123;<span class="comment">// t1, t2, t3</span></span><br><span class="line">        <span class="comment">// 获取许可</span></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            semaphore.acquire(); <span class="comment">// 没有许可的线程，在此等待</span></span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; poolSize; i++) &#123;</span><br><span class="line">            <span class="comment">// 获取空闲连接</span></span><br><span class="line">            <span class="keyword">if</span>(states.get(i) == <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="keyword">if</span> (states.compareAndSet(i, <span class="number">0</span>, <span class="number">1</span>)) &#123;</span><br><span class="line">                    log.debug(<span class="string">&quot;borrow &#123;&#125;&quot;</span>, connections[i]);</span><br><span class="line">                    <span class="keyword">return</span> connections[i];</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 不会执行到这里</span></span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 6. 归还连接</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">free</span><span class="params">(Connection conn)</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; poolSize; i++) &#123;</span><br><span class="line">            <span class="keyword">if</span> (connections[i] == conn) &#123;</span><br><span class="line">                states.set(i, <span class="number">0</span>);</span><br><span class="line">                log.debug(<span class="string">&quot;free &#123;&#125;&quot;</span>, conn);</span><br><span class="line">                semaphore.release();</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h5><p><strong>加锁解锁流程</strong></p>
<p>Semaphore 有点像一个停车场，permits 就好像停车位数量，当线程获得了 permits 就像是获得了停车位，然后 停车场显示空余车位减一</p>
<p>刚开始，permits（state）为 3，这时 5 个线程来获取资源</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220419103513420.png" alt="image-20220419103513420"></p>
<p>假设其中 Thread-1，Thread-2，Thread-4 cas 竞争成功，而 Thread-0 和 Thread-3 竞争失败，进入 AQS 队列 park 阻塞</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220419103537202.png" alt="image-20220419103537202"></p>
<p>这时 Thread-4 释放了 permits，状态如下</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220419103622104.png" alt="image-20220419103622104"></p>
<p>接下来 Thread-0 竞争成功，permits 再次设置为 0，设置自己为 head 节点，断开原来的 head 节点，unpark 接 下来的 Thread-3 节点，但由于 permits 是 0，因此 Thread-3 在尝试不成功后再次进入 park 状态</p>
<p><strong>源码分析</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">NonfairSync</span> <span class="keyword">extends</span> <span class="title class_">Sync</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">long</span> <span class="variable">serialVersionUID</span> <span class="operator">=</span> -<span class="number">2694183684443567898L</span>;</span><br><span class="line">    NonfairSync(<span class="type">int</span> permits) &#123;</span><br><span class="line">        <span class="comment">// permits 即 state</span></span><br><span class="line">        <span class="built_in">super</span>(permits);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Semaphore 方法, 方便阅读, 放在此处</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">acquire</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        sync.acquireSharedInterruptibly(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// AQS 继承过来的方法, 方便阅读, 放在此处</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title function_">acquireSharedInterruptibly</span><span class="params">(<span class="type">int</span> arg)</span></span><br><span class="line">        <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        <span class="keyword">if</span> (Thread.interrupted())</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">InterruptedException</span>();</span><br><span class="line">        <span class="keyword">if</span> (tryAcquireShared(arg) &lt; <span class="number">0</span>)</span><br><span class="line">            doAcquireSharedInterruptibly(arg);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 尝试获得共享锁</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="type">int</span> <span class="title function_">tryAcquireShared</span><span class="params">(<span class="type">int</span> acquires)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> nonfairTryAcquireShared(acquires);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Sync 继承过来的方法, 方便阅读, 放在此处</span></span><br><span class="line">    <span class="keyword">final</span> <span class="type">int</span> <span class="title function_">nonfairTryAcquireShared</span><span class="params">(<span class="type">int</span> acquires)</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">available</span> <span class="operator">=</span> getState();</span><br><span class="line">            <span class="type">int</span> <span class="variable">remaining</span> <span class="operator">=</span> available - acquires; </span><br><span class="line">            <span class="keyword">if</span> (</span><br><span class="line">                <span class="comment">// 如果许可已经用完, 返回负数, 表示获取失败, 进入 doAcquireSharedInterruptibly</span></span><br><span class="line">                remaining &lt; <span class="number">0</span> ||</span><br><span class="line">                <span class="comment">// 如果 cas 重试成功, 返回正数, 表示获取成功</span></span><br><span class="line">                compareAndSetState(available, remaining)</span><br><span class="line">            ) &#123;</span><br><span class="line">                <span class="keyword">return</span> remaining;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// AQS 继承过来的方法, 方便阅读, 放在此处</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">doAcquireSharedInterruptibly</span><span class="params">(<span class="type">int</span> arg)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        <span class="keyword">final</span> <span class="type">Node</span> <span class="variable">node</span> <span class="operator">=</span> addWaiter(Node.SHARED);</span><br><span class="line">        <span class="type">boolean</span> <span class="variable">failed</span> <span class="operator">=</span> <span class="literal">true</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">                <span class="keyword">final</span> <span class="type">Node</span> <span class="variable">p</span> <span class="operator">=</span> node.predecessor();</span><br><span class="line">                <span class="keyword">if</span> (p == head) &#123;</span><br><span class="line">                    <span class="comment">// 再次尝试获取许可</span></span><br><span class="line">                    <span class="type">int</span> <span class="variable">r</span> <span class="operator">=</span> tryAcquireShared(arg);</span><br><span class="line">                    <span class="keyword">if</span> (r &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">                        <span class="comment">// 成功后本线程出队（AQS）, 所在 Node设置为 head</span></span><br><span class="line">                        <span class="comment">// 如果 head.waitStatus == Node.SIGNAL ==&gt; 0 成功, 下一个节点 unpark</span></span><br><span class="line">                        <span class="comment">// 如果 head.waitStatus == 0 ==&gt; Node.PROPAGATE </span></span><br><span class="line">                        <span class="comment">// r 表示可用资源数, 为 0 则不会继续传播</span></span><br><span class="line">                        setHeadAndPropagate(node, r);</span><br><span class="line">                        p.next = <span class="literal">null</span>; <span class="comment">// help GC</span></span><br><span class="line">                        failed = <span class="literal">false</span>;</span><br><span class="line">                        <span class="keyword">return</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="comment">// 不成功, 设置上一个节点 waitStatus = Node.SIGNAL, 下轮进入 park 阻塞</span></span><br><span class="line">                <span class="keyword">if</span> (shouldParkAfterFailedAcquire(p, node) &amp;&amp;</span><br><span class="line">                    parkAndCheckInterrupt())</span><br><span class="line">                    <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">InterruptedException</span>();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            <span class="keyword">if</span> (failed)</span><br><span class="line">                cancelAcquire(node);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Semaphore 方法, 方便阅读, 放在此处</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">release</span><span class="params">()</span> &#123;</span><br><span class="line">        sync.releaseShared(<span class="number">1</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// AQS 继承过来的方法, 方便阅读, 放在此处</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> <span class="type">boolean</span> <span class="title function_">releaseShared</span><span class="params">(<span class="type">int</span> arg)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (tryReleaseShared(arg)) &#123;</span><br><span class="line">            doReleaseShared();</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Sync 继承过来的方法, 方便阅读, 放在此处</span></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">final</span> <span class="type">boolean</span> <span class="title function_">tryReleaseShared</span><span class="params">(<span class="type">int</span> releases)</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">current</span> <span class="operator">=</span> getState();</span><br><span class="line">            <span class="type">int</span> <span class="variable">next</span> <span class="operator">=</span> current + releases;</span><br><span class="line">            <span class="keyword">if</span> (next &lt; current) <span class="comment">// overflow</span></span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">&quot;Maximum permit count exceeded&quot;</span>);</span><br><span class="line">            <span class="keyword">if</span> (compareAndSetState(current, next))</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="CountdownLatch"><a href="#CountdownLatch" class="headerlink" title="CountdownLatch"></a>CountdownLatch</h4><p>用来进行线程同步协作，等待所有线程完成倒计时。 </p>
<h5 id="用法-1"><a href="#用法-1" class="headerlink" title="用法"></a>用法</h5><p>其中构造参数用来初始化等待计数值，await() 用来等待计数归零，countDown() 用来让计数减一</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">    <span class="type">CountDownLatch</span> <span class="variable">latch</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CountDownLatch</span>(<span class="number">3</span>);</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        log.debug(<span class="string">&quot;begin...&quot;</span>);</span><br><span class="line">        sleep(<span class="number">1</span>);</span><br><span class="line">        latch.countDown();</span><br><span class="line">        log.debug(<span class="string">&quot;end...&#123;&#125;&quot;</span>, latch.getCount());</span><br><span class="line">    &#125;).start();</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        log.debug(<span class="string">&quot;begin...&quot;</span>);</span><br><span class="line">        sleep(<span class="number">2</span>);</span><br><span class="line">        latch.countDown();</span><br><span class="line">        log.debug(<span class="string">&quot;end...&#123;&#125;&quot;</span>, latch.getCount());</span><br><span class="line">    &#125;).start();</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        log.debug(<span class="string">&quot;begin...&quot;</span>);</span><br><span class="line">        sleep(<span class="number">1.5</span>);</span><br><span class="line">        latch.countDown();</span><br><span class="line">        log.debug(<span class="string">&quot;end...&#123;&#125;&quot;</span>, latch.getCount());</span><br><span class="line">    &#125;).start();</span><br><span class="line">    log.debug(<span class="string">&quot;waiting...&quot;</span>);</span><br><span class="line">    latch.await();</span><br><span class="line">    log.debug(<span class="string">&quot;wait end...&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>实际应用不会创建线程，都是使用线程池。线程池中线程不会停止，所以不能使用join()方法。可以配合线程池使用，改进如下：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">    <span class="type">CountDownLatch</span> <span class="variable">latch</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CountDownLatch</span>(<span class="number">3</span>);</span><br><span class="line">    <span class="type">ExecutorService</span> <span class="variable">service</span> <span class="operator">=</span> Executors.newFixedThreadPool(<span class="number">4</span>);</span><br><span class="line">    service.submit(() -&gt; &#123;</span><br><span class="line">        log.debug(<span class="string">&quot;begin...&quot;</span>);</span><br><span class="line">        sleep(<span class="number">1</span>);</span><br><span class="line">        latch.countDown();</span><br><span class="line">        log.debug(<span class="string">&quot;end...&#123;&#125;&quot;</span>, latch.getCount());</span><br><span class="line">    &#125;);</span><br><span class="line">    service.submit(() -&gt; &#123;</span><br><span class="line">        log.debug(<span class="string">&quot;begin...&quot;</span>);</span><br><span class="line">        sleep(<span class="number">1.5</span>);</span><br><span class="line">        latch.countDown();</span><br><span class="line">        log.debug(<span class="string">&quot;end...&#123;&#125;&quot;</span>, latch.getCount());</span><br><span class="line">    &#125;);</span><br><span class="line">    service.submit(() -&gt; &#123;</span><br><span class="line">        log.debug(<span class="string">&quot;begin...&quot;</span>);</span><br><span class="line">        sleep(<span class="number">2</span>);</span><br><span class="line">        latch.countDown();</span><br><span class="line">        log.debug(<span class="string">&quot;end...&#123;&#125;&quot;</span>, latch.getCount());</span><br><span class="line">    &#125;);</span><br><span class="line">    service.submit(()-&gt;&#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            log.debug(<span class="string">&quot;waiting...&quot;</span>);</span><br><span class="line">            latch.await();</span><br><span class="line">            log.debug(<span class="string">&quot;wait end...&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="应用-2"><a href="#应用-2" class="headerlink" title="应用"></a>应用</h5><p>应用之同步等待多线程准备完毕</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">AtomicInteger</span> <span class="variable">num</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AtomicInteger</span>(<span class="number">0</span>);</span><br><span class="line"><span class="type">ExecutorService</span> <span class="variable">service</span> <span class="operator">=</span> Executors.newFixedThreadPool(<span class="number">10</span>, (r) -&gt; &#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(r, <span class="string">&quot;t&quot;</span> + num.getAndIncrement());</span><br><span class="line">&#125;);</span><br><span class="line"><span class="type">CountDownLatch</span> <span class="variable">latch</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CountDownLatch</span>(<span class="number">10</span>);</span><br><span class="line">String[] all = <span class="keyword">new</span> <span class="title class_">String</span>[<span class="number">10</span>];</span><br><span class="line"><span class="type">Random</span> <span class="variable">r</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Random</span>();</span><br><span class="line"><span class="keyword">for</span> (<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">0</span>; j &lt; <span class="number">10</span>; j++) &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">x</span> <span class="operator">=</span> j;</span><br><span class="line">    service.submit(() -&gt; &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt;= <span class="number">100</span>; i++) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                Thread.sleep(r.nextInt(<span class="number">100</span>));</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">            &#125;</span><br><span class="line">            all[x] = Thread.currentThread().getName() + <span class="string">&quot;(&quot;</span> + (i + <span class="string">&quot;%&quot;</span>) + <span class="string">&quot;)&quot;</span>;</span><br><span class="line">            System.out.print(<span class="string">&quot;\r&quot;</span> + Arrays.toString(all));</span><br><span class="line">        &#125;</span><br><span class="line">        latch.countDown();</span><br><span class="line">    &#125;);</span><br><span class="line">&#125;</span><br><span class="line">latch.await();</span><br><span class="line">System.out.println(<span class="string">&quot;\n游戏开始...&quot;</span>);</span><br><span class="line">service.shutdown();  </span><br></pre></td></tr></table></figure>

<p>中间输出</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">[t0(<span class="number">52</span>%), t1(<span class="number">47</span>%), t2(<span class="number">51</span>%), t3(<span class="number">40</span>%), t4(<span class="number">49</span>%), t5(<span class="number">44</span>%), t6(<span class="number">49</span>%), t7(<span class="number">52</span>%), t8(<span class="number">46</span>%), t9(<span class="number">46</span>%)] </span><br></pre></td></tr></table></figure>

<p>最后输出</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">[t0(<span class="number">100</span>%), t1(<span class="number">100</span>%), t2(<span class="number">100</span>%), t3(<span class="number">100</span>%), t4(<span class="number">100</span>%), t5(<span class="number">100</span>%), t6(<span class="number">100</span>%), t7(<span class="number">100</span>%), t8(<span class="number">100</span>%), </span><br><span class="line">t9(<span class="number">100</span>%)] </span><br><span class="line">游戏开始... </span><br></pre></td></tr></table></figure>

<h4 id="CyclicBarrier"><a href="#CyclicBarrier" class="headerlink" title="CyclicBarrier"></a>CyclicBarrier</h4><p>循环栅栏，用来进行线程协作，等待线程满足某个计数。构造时设置『计数个数』，每个线程执行到某个需要“同步”的时刻调用 await() 方法进行等待，当等待的线程数满足『计数个数』时，继续执行</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">CyclicBarrier</span> <span class="variable">cb</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CyclicBarrier</span>(<span class="number">2</span>); <span class="comment">// 个数为2时才会继续执行</span></span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Thread</span>(()-&gt;&#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;线程1开始..&quot;</span>+<span class="keyword">new</span> <span class="title class_">Date</span>());</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        cb.await(); <span class="comment">// 调用一次await()计数一次</span></span><br><span class="line">    &#125; <span class="keyword">catch</span> (InterruptedException | BrokenBarrierException e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125;</span><br><span class="line">    System.out.println(<span class="string">&quot;线程1继续向下运行...&quot;</span>+<span class="keyword">new</span> <span class="title class_">Date</span>());</span><br><span class="line">&#125;).start();</span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Thread</span>(()-&gt;&#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;线程2开始..&quot;</span>+<span class="keyword">new</span> <span class="title class_">Date</span>());</span><br><span class="line">    <span class="keyword">try</span> &#123; Thread.sleep(<span class="number">2000</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; &#125;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        cb.await(); <span class="comment">// 2 秒后，线程个数够2，继续运行</span></span><br><span class="line">    &#125; <span class="keyword">catch</span> (InterruptedException | BrokenBarrierException e) &#123;</span><br><span class="line">        e.printStackTrace();</span><br><span class="line">    &#125;</span><br><span class="line">    System.out.println(<span class="string">&quot;线程2继续向下运行...&quot;</span>+<span class="keyword">new</span> <span class="title class_">Date</span>());</span><br><span class="line">&#125;).start();</span><br></pre></td></tr></table></figure>

<blockquote>
<p>注意：</p>
<p>CyclicBarrier 与 CountDownLatch 的主要区别在于 CyclicBarrier 是可以重用的，可以循环使用 </p>
<p>线程池线程数要与计数一致</p>
</blockquote>
<h4 id="线程安全集合类"><a href="#线程安全集合类" class="headerlink" title="线程安全集合类"></a>线程安全集合类</h4><p>线程安全集合类可以分为三大类： </p>
<ul>
<li>遗留的线程安全集合如 Hashtable ， Vector </li>
<li>使用 Collections 装饰的线程安全集合，如： <ul>
<li>Collections.synchronizedCollection </li>
<li>Collections.synchronizedList</li>
<li>Collections.synchronizedMap</li>
<li>Collections.synchronizedSet</li>
<li>Collections.synchronizedNavigableMap</li>
<li>Collections.synchronizedNavigableSet </li>
<li>Collections.synchronizedSortedMap</li>
<li>Collections.synchronizedSortedSet</li>
</ul>
</li>
<li>java.util.concurrent.*</li>
</ul>
<p>java.util.concurrent.* 下的线程安全集合类，里面包含三类关键词： Blocking、CopyOnWrite、Concurrent</p>
<ul>
<li>Blocking 大部分实现基于锁，并提供用来阻塞的方法</li>
<li>CopyOnWrite 之类容器修改开销相对较重</li>
<li>Concurrent 类型的容器<ul>
<li>内部很多操作使用 cas 优化，一般可以提供较高吞吐量</li>
<li>弱一致性<ul>
<li>遍历时弱一致性，例如，当利用迭代器遍历时，如果容器发生修改，迭代器仍然可以继续进行遍历，这时内容是旧的</li>
<li>求大小弱一致性，size 操作未必是 100% 准确</li>
<li>读取弱一致性</li>
</ul>
</li>
</ul>
</li>
</ul>
<blockquote>
<p>遍历时如果发生了修改，对于非安全容器来讲，使用 fail-fast 机制也就是让遍历立刻失败，抛出 ConcurrentModificationException，不再继续遍历</p>
</blockquote>
<h4 id="ConcurrentHashMap"><a href="#ConcurrentHashMap" class="headerlink" title="ConcurrentHashMap"></a>ConcurrentHashMap</h4><h5 id="JDK-7-HashMap-并发死链问题"><a href="#JDK-7-HashMap-并发死链问题" class="headerlink" title="JDK 7 HashMap 并发死链问题"></a>JDK 7 HashMap 并发死链问题</h5><p>在 HashMap 源码 590 行加断点，目的是让 HashMap 在扩容为 32 时，并且线程为 Thread-0 或 Thread-1 时停下来</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="variable">newCapacity</span> <span class="operator">=</span> newTable.length;</span><br></pre></td></tr></table></figure>

<p>在 HashMap 源码 594 行加断点，这是为了观察 e 节点和 next 节点的状态</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">Entry&lt;K,V&gt; next = e.next; <span class="comment">// 593</span></span><br><span class="line"><span class="keyword">if</span> (rehash) <span class="comment">// 594</span></span><br><span class="line"><span class="comment">// ...</span></span><br></pre></td></tr></table></figure>

<p>这时可以在 Variables 面板观察到 e 和 next 变量</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">e (1)-&gt;(35)-&gt;(16)-&gt;null </span><br><span class="line">next (35)-&gt;(16)-&gt;null </span><br></pre></td></tr></table></figure>

<p>在 Threads 面板选中 Thread-1 恢复运行，可以看到控制台输出新的内容如下，Thread-1 扩容已完成</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">newTable[1] (35)-&gt;(1)-&gt;null </span><br></pre></td></tr></table></figure>

<p>这时 Thread-0 还停在 594 处， Variables 面板变量的状态已经变化为</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">e (1)-&gt;null </span><br><span class="line">next (35)-&gt;(1)-&gt;null </span><br></pre></td></tr></table></figure>

<p>因为 Thread-1 扩容时链表也是后加入的元素放入链表头，因此链表就倒过来了，但 Thread-1 虽然结 果正确，但它结束后 Thread-0 还要继续运行</p>
<p>接下来就可以单步调试（F8）观察死链的产生了，下一轮循环到 594，将 e 搬迁到 newTable 链表头</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">newTable[1] (1)-&gt;(35)-&gt;(1) ..死链了</span><br></pre></td></tr></table></figure>

<p>再看看源码</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">void</span> <span class="title function_">transfer</span><span class="params">(Entry[] newTable, <span class="type">boolean</span> rehash)</span> &#123; </span><br><span class="line">    <span class="type">int</span> <span class="variable">newCapacity</span> <span class="operator">=</span> newTable.length;</span><br><span class="line">    <span class="keyword">for</span> (Entry&lt;K,V&gt; e : table) &#123;</span><br><span class="line">        <span class="keyword">while</span>(<span class="literal">null</span> != e) &#123;</span><br><span class="line">            Entry&lt;K,V&gt; next = e.next;</span><br><span class="line">            <span class="comment">// 1 处</span></span><br><span class="line">            <span class="keyword">if</span> (rehash) &#123;</span><br><span class="line">                e.hash = <span class="literal">null</span> == e.key ? <span class="number">0</span> : hash(e.key);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> indexFor(e.hash, newCapacity);</span><br><span class="line">            <span class="comment">// 2 处</span></span><br><span class="line">            <span class="comment">// 将新元素加入 newTable[i], 原 newTable[i] 作为新元素的 next</span></span><br><span class="line">            e.next = newTable[i];</span><br><span class="line">            newTable[i] = e;</span><br><span class="line">            e = next;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>究其原因，是因为在多线程环境下使用了非线程安全的 map 集合 </p>
<p>JDK 8 虽然将扩容算法做了调整，不再将元素加入链表头（而是保持与扩容前一样的顺序），但仍不意味着能够在多线程环境下能够安全扩容，还会出现其它问题（如扩容丢数据）</p>
<h5 id="JDK-8-ConcurrentHashMap"><a href="#JDK-8-ConcurrentHashMap" class="headerlink" title="JDK 8 ConcurrentHashMap"></a>JDK 8 ConcurrentHashMap</h5><p>重要属性和内部类</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 默认为 0</span></span><br><span class="line"><span class="comment">// 当初始化时, 为 -1</span></span><br><span class="line"><span class="comment">// 当扩容时, 为 -(1 + 扩容线程数)</span></span><br><span class="line"><span class="comment">// 当初始化或扩容完成后，为 下一次的扩容的阈值大小</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">transient</span> <span class="keyword">volatile</span> <span class="type">int</span> sizeCtl;</span><br><span class="line"><span class="comment">// 整个 ConcurrentHashMap 就是一个 Node[]</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Node</span>&lt;K,V&gt; <span class="keyword">implements</span> <span class="title class_">Map</span>.Entry&lt;K,V&gt; &#123;&#125;</span><br><span class="line"><span class="comment">// hash 表</span></span><br><span class="line"><span class="keyword">transient</span> <span class="keyword">volatile</span> Node&lt;K,V&gt;[] table;</span><br><span class="line"><span class="comment">// 扩容时的 新 hash 表</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">transient</span> <span class="keyword">volatile</span> Node&lt;K,V&gt;[] nextTable;</span><br><span class="line"><span class="comment">// 扩容时如果某个 bin 迁移完毕, 用 ForwardingNode 作为旧 table bin 的头结点</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">ForwardingNode</span>&lt;K,V&gt; <span class="keyword">extends</span> <span class="title class_">Node</span>&lt;K,V&gt; &#123;&#125;</span><br><span class="line"><span class="comment">// 用在 compute 以及 computeIfAbsent 时, 用来占位, 计算完成后替换为普通 Node</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">ReservationNode</span>&lt;K,V&gt; <span class="keyword">extends</span> <span class="title class_">Node</span>&lt;K,V&gt; &#123;&#125;</span><br><span class="line"><span class="comment">// 作为 treebin 的头节点, 存储 root 和 first</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">TreeBin</span>&lt;K,V&gt; <span class="keyword">extends</span> <span class="title class_">Node</span>&lt;K,V&gt; &#123;&#125;</span><br><span class="line"><span class="comment">// 作为 treebin 的节点, 存储 parent, left, right</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">TreeNode</span>&lt;K,V&gt; <span class="keyword">extends</span> <span class="title class_">Node</span>&lt;K,V&gt; &#123;&#125;</span><br></pre></td></tr></table></figure>

<p>ForwardingNode挂在头节点，表示已经处理过了，其他线程就不会再来处理</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220420091833438.png" alt="image-20220420091833438"></p>
<p>重要方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 获取 Node[] 中第 i 个 Node</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> &lt;K,V&gt; Node&lt;K,V&gt; <span class="title function_">tabAt</span><span class="params">(Node&lt;K,V&gt;[] tab, <span class="type">int</span> i)</span></span><br><span class="line"><span class="comment">// cas 修改 Node[] 中第 i 个 Node 的值, c 为旧值, v 为新值</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> &lt;K,V&gt; <span class="type">boolean</span> <span class="title function_">casTabAt</span><span class="params">(Node&lt;K,V&gt;[] tab, <span class="type">int</span> i, Node&lt;K,V&gt; c, Node&lt;K,V&gt; v)</span></span><br><span class="line"><span class="comment">// 直接修改 Node[] 中第 i 个 Node 的值, v 为新值</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> &lt;K,V&gt; <span class="keyword">void</span> <span class="title function_">setTabAt</span><span class="params">(Node&lt;K,V&gt;[] tab, <span class="type">int</span> i, Node&lt;K,V&gt; v)</span></span><br></pre></td></tr></table></figure>

<p>构造器分析</p>
<p>可以看到实现了懒惰初始化，在构造方法中仅仅计算了 table 的大小，以后在第一次使用时才会真正创建</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//concurrencyLevel并发度</span></span><br><span class="line"><span class="keyword">public</span> <span class="title function_">ConcurrentHashMap</span><span class="params">(<span class="type">int</span> initialCapacity, <span class="type">float</span> loadFactor, <span class="type">int</span> concurrencyLevel)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (!(loadFactor &gt; <span class="number">0.0f</span>) || initialCapacity &lt; <span class="number">0</span> || concurrencyLevel &lt;= <span class="number">0</span>)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>();</span><br><span class="line">    <span class="keyword">if</span> (initialCapacity &lt; concurrencyLevel) <span class="comment">// Use at least as many bins</span></span><br><span class="line">        initialCapacity = concurrencyLevel; <span class="comment">// as estimated threads</span></span><br><span class="line">    <span class="type">long</span> <span class="variable">size</span> <span class="operator">=</span> (<span class="type">long</span>)(<span class="number">1.0</span> + (<span class="type">long</span>)initialCapacity / loadFactor);</span><br><span class="line">    <span class="comment">// tableSizeFor 仍然是保证计算的大小是 2^n, 即 16,32,64 ... </span></span><br><span class="line">    <span class="type">int</span> <span class="variable">cap</span> <span class="operator">=</span> (size &gt;= (<span class="type">long</span>)MAXIMUM_CAPACITY) ?</span><br><span class="line">        MAXIMUM_CAPACITY : tableSizeFor((<span class="type">int</span>)size);</span><br><span class="line">    <span class="built_in">this</span>.sizeCtl = cap;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>get 流程，无锁</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> V <span class="title function_">get</span><span class="params">(Object key)</span> &#123;</span><br><span class="line">    Node&lt;K,V&gt;[] tab; Node&lt;K,V&gt; e, p; <span class="type">int</span> n, eh; K ek;</span><br><span class="line">    <span class="comment">// spread 方法能确保返回结果是正数</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">h</span> <span class="operator">=</span> spread(key.hashCode());</span><br><span class="line">    <span class="keyword">if</span> ((tab = table) != <span class="literal">null</span> &amp;&amp; (n = tab.length) &gt; <span class="number">0</span> &amp;&amp;</span><br><span class="line">        (e = tabAt(tab, (n - <span class="number">1</span>) &amp; h)) != <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="comment">// 如果头结点已经是要查找的 key</span></span><br><span class="line">        <span class="keyword">if</span> ((eh = e.hash) == h) &#123;</span><br><span class="line">            <span class="keyword">if</span> ((ek = e.key) == key || (ek != <span class="literal">null</span> &amp;&amp; key.equals(ek)))</span><br><span class="line">                <span class="keyword">return</span> e.val;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// hash 为负数表示该 bin 在扩容中或是 treebin, 这时调用 find 方法来查找</span></span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (eh &lt; <span class="number">0</span>)</span><br><span class="line">            <span class="keyword">return</span> (p = e.find(h, key)) != <span class="literal">null</span> ? p.val : <span class="literal">null</span>;</span><br><span class="line">        <span class="comment">// 正常遍历链表, 用 equals 比较</span></span><br><span class="line">        <span class="keyword">while</span> ((e = e.next) != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (e.hash == h &amp;&amp;</span><br><span class="line">                ((ek = e.key) == key || (ek != <span class="literal">null</span> &amp;&amp; key.equals(ek))))</span><br><span class="line">                <span class="keyword">return</span> e.val;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>put 流程</strong></p>
<p>以下数组简称（table），链表简称（bin）</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> V <span class="title function_">put</span><span class="params">(K key, V value)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> putVal(key, value, <span class="literal">false</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">final</span> V <span class="title function_">putVal</span><span class="params">(K key, V value, <span class="type">boolean</span> onlyIfAbsent)</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> (key == <span class="literal">null</span> || value == <span class="literal">null</span>) <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">NullPointerException</span>();</span><br><span class="line">    <span class="comment">// 其中 spread 方法会综合高位低位, 具有更好的 hash 性</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">hash</span> <span class="operator">=</span> spread(key.hashCode());</span><br><span class="line">    <span class="type">int</span> <span class="variable">binCount</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (Node&lt;K,V&gt;[] tab = table;;) &#123;</span><br><span class="line">        <span class="comment">// f 是链表头节点</span></span><br><span class="line">        <span class="comment">// fh 是链表头结点的 hash</span></span><br><span class="line">        <span class="comment">// i 是链表在 table 中的下标</span></span><br><span class="line">        Node&lt;K,V&gt; f; <span class="type">int</span> n, i, fh;</span><br><span class="line">        <span class="comment">// 要创建 table</span></span><br><span class="line">        <span class="keyword">if</span> (tab == <span class="literal">null</span> || (n = tab.length) == <span class="number">0</span>)</span><br><span class="line">            <span class="comment">// 初始化 table 使用了 cas, 无需 synchronized 创建成功, 进入下一轮循环</span></span><br><span class="line">            tab = initTable();</span><br><span class="line">        <span class="comment">// 要创建链表头节点</span></span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> ((f = tabAt(tab, i = (n - <span class="number">1</span>) &amp; hash)) == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="comment">// 添加链表头使用了 cas, 无需 synchronized</span></span><br><span class="line">            <span class="keyword">if</span> (casTabAt(tab, i, <span class="literal">null</span>,</span><br><span class="line">                         <span class="keyword">new</span> <span class="title class_">Node</span>&lt;K,V&gt;(hash, key, value, <span class="literal">null</span>)))</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 帮忙扩容</span></span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> ((fh = f.hash) == MOVED)</span><br><span class="line">            <span class="comment">// 帮忙之后, 进入下一轮循环</span></span><br><span class="line">            tab = helpTransfer(tab, f);</span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="type">V</span> <span class="variable">oldVal</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">            <span class="comment">// 锁住链表头节点</span></span><br><span class="line">            <span class="keyword">synchronized</span> (f) &#123;</span><br><span class="line">                <span class="comment">// 再次确认链表头节点没有被移动</span></span><br><span class="line">                <span class="keyword">if</span> (tabAt(tab, i) == f) &#123;</span><br><span class="line">                    <span class="comment">// 链表</span></span><br><span class="line">                    <span class="keyword">if</span> (fh &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">                        binCount = <span class="number">1</span>;</span><br><span class="line">                        <span class="comment">// 遍历链表</span></span><br><span class="line">                        <span class="keyword">for</span> (Node&lt;K,V&gt; e = f;; ++binCount) &#123;</span><br><span class="line">                            K ek;</span><br><span class="line">                            <span class="comment">// 找到相同的 key</span></span><br><span class="line">                            <span class="keyword">if</span> (e.hash == hash &amp;&amp;</span><br><span class="line">                                ((ek = e.key) == key ||</span><br><span class="line">                                 (ek != <span class="literal">null</span> &amp;&amp; key.equals(ek)))) &#123;</span><br><span class="line">                                oldVal = e.val;</span><br><span class="line">                                <span class="comment">// 更新</span></span><br><span class="line">                                <span class="keyword">if</span> (!onlyIfAbsent)</span><br><span class="line">                                    e.val = value;</span><br><span class="line">                                <span class="keyword">break</span>;</span><br><span class="line">                            &#125;</span><br><span class="line">                            Node&lt;K,V&gt; pred = e;</span><br><span class="line">                            <span class="comment">// 已经是最后的节点了, 新增 Node, 追加至链表尾</span></span><br><span class="line">                            <span class="keyword">if</span> ((e = e.next) == <span class="literal">null</span>) &#123;</span><br><span class="line">                                pred.next = <span class="keyword">new</span> <span class="title class_">Node</span>&lt;K,V&gt;(hash, key,</span><br><span class="line">                                                          value, <span class="literal">null</span>);</span><br><span class="line">                                <span class="keyword">break</span>;</span><br><span class="line">                            &#125;</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                    <span class="comment">// 红黑树</span></span><br><span class="line">                    <span class="keyword">else</span> <span class="keyword">if</span> (f <span class="keyword">instanceof</span> TreeBin) &#123;</span><br><span class="line">                        Node&lt;K,V&gt; p;</span><br><span class="line">                        binCount = <span class="number">2</span>;</span><br><span class="line">                        <span class="comment">// putTreeVal 会看 key 是否已经在树中, 是, 则返回对应的 TreeNode</span></span><br><span class="line">                        <span class="keyword">if</span> ((p = ((TreeBin&lt;K,V&gt;)f).putTreeVal(hash, key,</span><br><span class="line">                                                              value)) != <span class="literal">null</span>) &#123;</span><br><span class="line">                            oldVal = p.val;</span><br><span class="line">                            <span class="keyword">if</span> (!onlyIfAbsent)</span><br><span class="line">                                p.val = value;</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="comment">// 释放链表头节点的锁</span></span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (binCount != <span class="number">0</span>) &#123; </span><br><span class="line">                <span class="keyword">if</span> (binCount &gt;= TREEIFY_THRESHOLD)</span><br><span class="line">                    <span class="comment">// 如果链表长度 &gt;= 树化阈值(8), 进行链表转为红黑树</span></span><br><span class="line">                    treeifyBin(tab, i);</span><br><span class="line">                <span class="keyword">if</span> (oldVal != <span class="literal">null</span>)</span><br><span class="line">                    <span class="keyword">return</span> oldVal;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 增加 size 计数</span></span><br><span class="line">    addCount(<span class="number">1L</span>, binCount);</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> Node&lt;K,V&gt;[] initTable() &#123;</span><br><span class="line">    Node&lt;K,V&gt;[] tab; <span class="type">int</span> sc;</span><br><span class="line">    <span class="keyword">while</span> ((tab = table) == <span class="literal">null</span> || tab.length == <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> ((sc = sizeCtl) &lt; <span class="number">0</span>)</span><br><span class="line">            Thread.yield();</span><br><span class="line">        <span class="comment">// 尝试将 sizeCtl 设置为 -1（表示初始化 table）</span></span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (U.compareAndSwapInt(<span class="built_in">this</span>, SIZECTL, sc, -<span class="number">1</span>)) &#123;</span><br><span class="line">            <span class="comment">// 获得锁, 创建 table, 这时其它线程会在 while() 循环中 yield 直至 table 创建</span></span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="keyword">if</span> ((tab = table) == <span class="literal">null</span> || tab.length == <span class="number">0</span>) &#123;</span><br><span class="line">                    <span class="type">int</span> <span class="variable">n</span> <span class="operator">=</span> (sc &gt; <span class="number">0</span>) ? sc : DEFAULT_CAPACITY;</span><br><span class="line">                    Node&lt;K,V&gt;[] nt = (Node&lt;K,V&gt;[])<span class="keyword">new</span> <span class="title class_">Node</span>&lt;?,?&gt;[n];</span><br><span class="line">                    table = tab = nt;</span><br><span class="line">                    sc = n - (n &gt;&gt;&gt; <span class="number">2</span>);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                sizeCtl = sc;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> tab;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// check 是之前 binCount 的个数</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title function_">addCount</span><span class="params">(<span class="type">long</span> x, <span class="type">int</span> check)</span> &#123;</span><br><span class="line">    CounterCell[] as; <span class="type">long</span> b, s;</span><br><span class="line">    <span class="keyword">if</span> (</span><br><span class="line">        <span class="comment">// 已经有了 counterCells, 向 cell 累加</span></span><br><span class="line">        (as = counterCells) != <span class="literal">null</span> ||</span><br><span class="line">        <span class="comment">// 还没有, 向 baseCount 累加</span></span><br><span class="line">        !U.compareAndSwapLong(<span class="built_in">this</span>, BASECOUNT, b = baseCount, s = b + x)</span><br><span class="line">    ) &#123;</span><br><span class="line">        CounterCell a; <span class="type">long</span> v; <span class="type">int</span> m;</span><br><span class="line">        <span class="type">boolean</span> <span class="variable">uncontended</span> <span class="operator">=</span> <span class="literal">true</span>;</span><br><span class="line">        <span class="keyword">if</span> (</span><br><span class="line">            <span class="comment">// 还没有 counterCells</span></span><br><span class="line">            as == <span class="literal">null</span> || (m = as.length - <span class="number">1</span>) &lt; <span class="number">0</span> ||</span><br><span class="line">            <span class="comment">// 还没有 cell</span></span><br><span class="line">            (a = as[ThreadLocalRandom.getProbe() &amp; m]) == <span class="literal">null</span> ||</span><br><span class="line">            <span class="comment">// cell cas 增加计数失败</span></span><br><span class="line">            !(uncontended = U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))</span><br><span class="line">        ) &#123;</span><br><span class="line">            <span class="comment">// 创建累加单元数组和cell, 累加重试</span></span><br><span class="line">            fullAddCount(x, uncontended);</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (check &lt;= <span class="number">1</span>)</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        <span class="comment">// 获取元素个数</span></span><br><span class="line">        s = sumCount();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (check &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">        Node&lt;K,V&gt;[] tab, nt; <span class="type">int</span> n, sc;</span><br><span class="line">        <span class="keyword">while</span> (s &gt;= (<span class="type">long</span>)(sc = sizeCtl) &amp;&amp; (tab = table) != <span class="literal">null</span> &amp;&amp;</span><br><span class="line">               (n = tab.length) &lt; MAXIMUM_CAPACITY) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">rs</span> <span class="operator">=</span> resizeStamp(n);</span><br><span class="line">            <span class="keyword">if</span> (sc &lt; <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="keyword">if</span> ((sc &gt;&gt;&gt; RESIZE_STAMP_SHIFT) != rs || sc == rs + <span class="number">1</span> ||</span><br><span class="line">                    sc == rs + MAX_RESIZERS || (nt = nextTable) == <span class="literal">null</span> ||</span><br><span class="line">                    transferIndex &lt;= <span class="number">0</span>)</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                <span class="comment">// newtable 已经创建了，帮忙扩容</span></span><br><span class="line">                <span class="keyword">if</span> (U.compareAndSwapInt(<span class="built_in">this</span>, SIZECTL, sc, sc + <span class="number">1</span>))</span><br><span class="line">                    transfer(tab, nt);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// 需要扩容，这时 newtable 未创建</span></span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (U.compareAndSwapInt(<span class="built_in">this</span>, SIZECTL, sc,</span><br><span class="line">                                         (rs &lt;&lt; RESIZE_STAMP_SHIFT) + <span class="number">2</span>)</span><br><span class="line">                     transfer(tab, <span class="literal">null</span>);</span><br><span class="line">                     s = sumCount();</span><br><span class="line">                     &#125;</span><br><span class="line">                     &#125;</span><br><span class="line">                     &#125;        </span><br></pre></td></tr></table></figure>

<p><strong>size 计算流程</strong></p>
<p>size 计算实际发生在 put，remove 改变集合元素的操作之中</p>
<ul>
<li>没有竞争发生，向 baseCount 累加计数</li>
<li>有竞争发生，新建 counterCells，向其中的一个 cell 累加计数<ul>
<li>counterCells 初始有两个 cell </li>
<li>如果计数竞争比较激烈，会创建新的 cell 来累加计数</li>
</ul>
</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">size</span><span class="params">()</span> &#123;</span><br><span class="line">    <span class="type">long</span> <span class="variable">n</span> <span class="operator">=</span> sumCount();</span><br><span class="line">    <span class="keyword">return</span> ((n &lt; <span class="number">0L</span>) ? <span class="number">0</span> :</span><br><span class="line">            (n &gt; (<span class="type">long</span>)Integer.MAX_VALUE) ? Integer.MAX_VALUE :</span><br><span class="line">            (<span class="type">int</span>)n);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">final</span> <span class="type">long</span> <span class="title function_">sumCount</span><span class="params">()</span> &#123;</span><br><span class="line">    CounterCell[] as = counterCells; CounterCell a;</span><br><span class="line">    <span class="comment">// 将 baseCount 计数与所有 cell 计数累加</span></span><br><span class="line">    <span class="type">long</span> <span class="variable">sum</span> <span class="operator">=</span> baseCount;</span><br><span class="line">    <span class="keyword">if</span> (as != <span class="literal">null</span>) &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; as.length; ++i) &#123;</span><br><span class="line">            <span class="keyword">if</span> ((a = as[i]) != <span class="literal">null</span>)</span><br><span class="line">                sum += a.value;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> sum;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>Java 8 数组（Node） +（ 链表 Node | 红黑树 TreeNode ） 以下数组简称（table），链表简称（bin）</p>
<ul>
<li>初始化，使用 cas 来保证并发安全，懒惰初始化 table</li>
<li>树化，当 table.length &lt; 64 时，先尝试扩容，超过 64 时，并且 bin.length &gt; 8 时，会将链表树化，树化过程会用 synchronized 锁住链表头</li>
<li>put，如果该 bin 尚未创建，只需要使用 cas 创建 bin；如果已经有了，锁住链表头进行后续 put 操作，元素添加至 bin 的尾部</li>
<li>get，无锁操作仅需要保证可见性，扩容过程中 get 操作拿到的是 ForwardingNode 它会让 get 操作在新table 进行搜索</li>
<li>扩容，扩容时以 bin 为单位进行，需要对 bin 进行 synchronized，但这时妙的是其它竞争线程也不是无事可做，它们会帮助把其它 bin 进行扩容，扩容时平均只有 1/6 的节点会把复制到新 table 中</li>
<li>size，元素个数保存在 baseCount 中，并发时的个数变动保存在 CounterCell[] 当中。最后统计数量时累加即可</li>
</ul>
<h5 id="JDK-7-ConcurrentHashMap"><a href="#JDK-7-ConcurrentHashMap" class="headerlink" title="JDK 7 ConcurrentHashMap"></a>JDK 7 ConcurrentHashMap</h5><p>它维护了一个 segment 数组，每个 segment 对应一把锁 </p>
<ul>
<li>优点：如果多个线程访问不同的 segment，实际是没有冲突的，这与 jdk8 中是类似的</li>
<li>缺点：Segments 数组默认大小为16，这个容量初始化指定后就不能改变了，并且不是懒惰初始化</li>
</ul>
<img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220420102307686.png" alt="image-20220420102307686" style="zoom:50%;" />

<p>segment 继承了可重入锁（ReentrantLock），它的 put 方法会加锁</p>
<p>rehash 流程发生在 put 中，因为此时已经获得了锁，因此 rehash 时不需要考虑线程安全</p>
<p>get 时并未加锁，用了 UNSAFE 方法保证了可见性，扩容过程中，get 先发生就从旧表取内容，get 后发生就从新表取内容</p>
<p>size 计算流程，计算元素个数前，先不加锁计算两次，如果前后两次结果如一样，认为个数正确返回 如果不一样，进行重试，重试次数超过 3，将所有 segment 锁住，重新计算个数返回</p>
<h4 id="BlockingQueue"><a href="#BlockingQueue" class="headerlink" title="BlockingQueue"></a>BlockingQueue</h4><p>LinkedBlockingQueue 原理</p>
<p><strong>基本的入队出队</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">LinkedBlockingQueue</span>&lt;E&gt; <span class="keyword">extends</span> <span class="title class_">AbstractQueue</span>&lt;E&gt;</span><br><span class="line">    <span class="keyword">implements</span> <span class="title class_">BlockingQueue</span>&lt;E&gt;, java.io.Serializable &#123;</span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Node</span>&lt;E&gt; &#123;</span><br><span class="line">        E item;</span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 下列三种情况之一</span></span><br><span class="line"><span class="comment"> * - 真正的后继节点</span></span><br><span class="line"><span class="comment"> * - 自己, 发生在出队时</span></span><br><span class="line"><span class="comment"> * - null, 表示是没有后继节点, 是最后了</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line">        Node&lt;E&gt; next;</span><br><span class="line">        Node(E x) &#123; item = x; &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;   </span><br></pre></td></tr></table></figure>

<p>初始化链表 last = head = new Node(null); Dummy 节点用来占位，item 为 null</p>
<img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220420104958198.png" alt="image-20220420104958198" style="zoom:50%;" />

<p>当一个节点入队 last = last.next = node;(包含两个赋值操作)</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220420105206867.png" alt="image-20220420105206867"></p>
<p>出队</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">Node&lt;E&gt; h = head;</span><br><span class="line">Node&lt;E&gt; first = h.next;</span><br><span class="line">h.next = h; <span class="comment">// help GC</span></span><br><span class="line">head = first;</span><br><span class="line"><span class="type">E</span> <span class="variable">x</span> <span class="operator">=</span> first.item;</span><br><span class="line">first.item = <span class="literal">null</span>;</span><br><span class="line"><span class="keyword">return</span> x;</span><br></pre></td></tr></table></figure>

<p>h = head</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220420105421530.png" alt="image-20220420105421530"></p>
<p>first = h.next</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220420105440880.png" alt="image-20220420105440880"></p>
<p>h.next = h</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220420105521262.png" alt="image-20220420105521262"></p>
<p>head = first</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220420105553094.png" alt="image-20220420105553094"></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">E</span> <span class="variable">x</span> <span class="operator">=</span> first.item;</span><br><span class="line">first.item = <span class="literal">null</span>;</span><br><span class="line"><span class="keyword">return</span> x;</span><br></pre></td></tr></table></figure>

<img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220420105658637.png" alt="image-20220420105658637" style="zoom:50%;" />

<p><strong>加锁分析</strong></p>
<p>==高明之处==在于用了两把锁和 dummy 节点</p>
<ul>
<li>用一把锁，同一时刻，最多只允许有一个线程（生产者或消费者，二选一）执行</li>
<li>用两把锁，同一时刻，可以允许两个线程同时（一个生产者与一个消费者）执行<ul>
<li>消费者与消费者线程仍然串行</li>
<li>生产者与生产者线程仍然串行</li>
</ul>
</li>
</ul>
<p>线程安全分析</p>
<ul>
<li>当节点总数大于 2 时（包括 dummy 节点），putLock 保证的是 last 节点的线程安全，takeLock 保证的是 head 节点的线程安全。两把锁保证了入队和出队没有竞争</li>
<li>当节点总数等于 2 时（即一个 dummy 节点，一个正常节点）这时候，仍然是两把锁锁两个对象，不会竞争</li>
<li>当节点总数等于 1 时（就一个 dummy 节点）这时 take 线程会被 notEmpty 条件阻塞，有竞争，会阻塞</li>
</ul>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 用于 put(阻塞) offer(非阻塞)</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> <span class="type">ReentrantLock</span> <span class="variable">putLock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReentrantLock</span>();</span><br><span class="line"><span class="comment">// 用户 take(阻塞) poll(非阻塞)</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> <span class="type">ReentrantLock</span> <span class="variable">takeLock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReentrantLock</span>();</span><br></pre></td></tr></table></figure>

<p>put 操作</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">put</span><span class="params">(E e)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">    <span class="keyword">if</span> (e == <span class="literal">null</span>) <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">NullPointerException</span>();<span class="comment">//不能为空</span></span><br><span class="line">    <span class="type">int</span> <span class="variable">c</span> <span class="operator">=</span> -<span class="number">1</span>;</span><br><span class="line">    Node&lt;E&gt; node = <span class="keyword">new</span> <span class="title class_">Node</span>&lt;E&gt;(e);</span><br><span class="line">    <span class="keyword">final</span> <span class="type">ReentrantLock</span> <span class="variable">putLock</span> <span class="operator">=</span> <span class="built_in">this</span>.putLock;</span><br><span class="line">    <span class="comment">// count 用来维护元素计数</span></span><br><span class="line">    <span class="keyword">final</span> <span class="type">AtomicInteger</span> <span class="variable">count</span> <span class="operator">=</span> <span class="built_in">this</span>.count;</span><br><span class="line">    putLock.lockInterruptibly();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 满了等待</span></span><br><span class="line">        <span class="keyword">while</span> (count.get() == capacity) &#123;</span><br><span class="line">            <span class="comment">// 倒过来读就好: 等待 notFull</span></span><br><span class="line">            notFull.await();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 有空位, 入队且计数加一</span></span><br><span class="line">        enqueue(node);</span><br><span class="line">        c = count.getAndIncrement(); </span><br><span class="line">        <span class="comment">// 除了自己 put 以外, 队列还有空位, 由自己叫醒其他 put 线程</span></span><br><span class="line">        <span class="keyword">if</span> (c + <span class="number">1</span> &lt; capacity)</span><br><span class="line">            notFull.signal();</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        putLock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 如果队列中有一个元素, 叫醒 take 线程</span></span><br><span class="line">    <span class="keyword">if</span> (c == <span class="number">0</span>)</span><br><span class="line">        <span class="comment">// 这里调用的是 notEmpty.signal() 而不是 notEmpty.signalAll() 是为了减少竞争</span></span><br><span class="line">        signalNotEmpty();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>take 操作</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> E <span class="title function_">take</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">    E x;</span><br><span class="line">    <span class="type">int</span> <span class="variable">c</span> <span class="operator">=</span> -<span class="number">1</span>;</span><br><span class="line">    <span class="keyword">final</span> <span class="type">AtomicInteger</span> <span class="variable">count</span> <span class="operator">=</span> <span class="built_in">this</span>.count;</span><br><span class="line">    <span class="keyword">final</span> <span class="type">ReentrantLock</span> <span class="variable">takeLock</span> <span class="operator">=</span> <span class="built_in">this</span>.takeLock;</span><br><span class="line">    takeLock.lockInterruptibly();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">while</span> (count.get() == <span class="number">0</span>) &#123;</span><br><span class="line">            notEmpty.await();</span><br><span class="line">        &#125;</span><br><span class="line">        x = dequeue();</span><br><span class="line">        c = count.getAndDecrement();</span><br><span class="line">        <span class="keyword">if</span> (c &gt; <span class="number">1</span>)</span><br><span class="line">            notEmpty.signal();</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        takeLock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 如果队列中只有一个空位时, 叫醒 put 线程</span></span><br><span class="line">    <span class="comment">// 如果有多个线程进行出队, 第一个线程满足 c == capacity, 但后续线程 c &lt; capacity</span></span><br><span class="line">    <span class="keyword">if</span> (c == capacity)</span><br><span class="line">        <span class="comment">// 这里调用的是 notFull.signal() 而不是 notFull.signalAll() 是为了减少竞争</span></span><br><span class="line">        signalNotFull()</span><br><span class="line">        <span class="keyword">return</span> x;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>性能比较</p>
<p>主要列举 LinkedBlockingQueue 与 ArrayBlockingQueue 的性能比较</p>
<ul>
<li>Linked 支持有界，Array 强制有界</li>
<li>Linked 实现是链表，Array 实现是数组</li>
<li>Linked 是懒惰的，而 Array 需要提前初始化Node 数组 </li>
<li>Linked 每次入队会生成新 Node，而 Array 的 Node 是提前创建好的</li>
<li>Linked 两把锁，Array 一把锁</li>
</ul>
<h4 id="ConcurrentLinkedQueue"><a href="#ConcurrentLinkedQueue" class="headerlink" title="ConcurrentLinkedQueue"></a>ConcurrentLinkedQueue</h4><p>ConcurrentLinkedQueue 的设计与 LinkedBlockingQueue 非常像，也是</p>
<ul>
<li>两把【锁】，同一时刻，可以允许两个线程同时（一个生产者与一个消费者）执行 </li>
<li>dummy 节点的引入让两把【锁】将来锁住的是不同对象，避免竞争</li>
<li>只是这【锁】使用了 cas 来实现</li>
</ul>
<p>事实上，ConcurrentLinkedQueue 应用还是非常广泛的 例如Tomcat 的 Connector 结构时，Acceptor 作为生产者向 Poller 消费者传递事件信息时，正是采用了 ConcurrentLinkedQueue 将 SocketChannel 给 Poller 使用</p>
<h4 id="CopyOnWriteArrayList"><a href="#CopyOnWriteArrayList" class="headerlink" title="CopyOnWriteArrayList"></a>CopyOnWriteArrayList</h4><p>CopyOnWriteArraySet 是它的马甲 底层实现采用了 写入时拷贝的思想，增删改操作会将底层数组拷贝一份，更改操作在新数组上执行，这时不影响其它线程的并发读，读写分离。 以新增为例：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">add</span><span class="params">(E e)</span> &#123;</span><br><span class="line">    <span class="keyword">synchronized</span> (lock) &#123;</span><br><span class="line">        <span class="comment">// 获取旧的数组</span></span><br><span class="line">        Object[] es = getArray();</span><br><span class="line">        <span class="type">int</span> <span class="variable">len</span> <span class="operator">=</span> es.length;</span><br><span class="line">        <span class="comment">// 拷贝新的数组（这里是比较耗时的操作，但不影响其它读线程）</span></span><br><span class="line">        es = Arrays.copyOf(es, len + <span class="number">1</span>);</span><br><span class="line">        <span class="comment">// 添加新元素</span></span><br><span class="line">        es[len] = e;</span><br><span class="line">        <span class="comment">// 替换旧的数组</span></span><br><span class="line">        setArray(es);</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>这里的源码版本是 Java 11，在 Java 1.8 中使用的是可重入锁而不是 synchronized</p>
</blockquote>
<p>其它读操作并未加锁，例如：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">forEach</span><span class="params">(Consumer&lt;? <span class="built_in">super</span> E&gt; action)</span> &#123;</span><br><span class="line">    Objects.requireNonNull(action);</span><br><span class="line">    <span class="keyword">for</span> (Object x : getArray()) &#123;</span><br><span class="line">        <span class="meta">@SuppressWarnings(&quot;unchecked&quot;)</span> <span class="type">E</span> <span class="variable">e</span> <span class="operator">=</span> (E) x;</span><br><span class="line">        action.accept(e);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>适合『读多写少』的应用场景</p>
<p>get 、迭代器都是弱一致性</p>
<blockquote>
<p>数据库的 MVCC 都是弱一致性的表现</p>
<p>并发高和一致性是矛盾的，需要权衡</p>
</blockquote>
<h2 id="并发编程的设计模式"><a href="#并发编程的设计模式" class="headerlink" title="并发编程的设计模式"></a>并发编程的设计模式</h2><h3 id="同步模式之保护性暂停"><a href="#同步模式之保护性暂停" class="headerlink" title="同步模式之保护性暂停"></a>同步模式之保护性暂停</h3><p>基于wait notify的应用</p>
<h4 id="定义"><a href="#定义" class="headerlink" title="定义"></a>定义</h4><p>即 Guarded Suspension，用在一个线程等待另一个线程的执行结果</p>
<p>要点</p>
<ul>
<li>有一个结果需要从一个线程传递到另一个线程，让他们关联同一个GuardedObject </li>
<li>如果有结果不断从一个线程到另一个线程那么可以使用消息队列（见生产者/消费者）</li>
<li>JDK 中，join 的实现、Future 的实现，采用的就是此模式</li>
<li>因为要等待另一方的结果，因此归类到同步模式</li>
</ul>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220413144556916.png" alt="image-20220413144556916"></p>
<h4 id="实现"><a href="#实现" class="headerlink" title="实现"></a>实现</h4><p>关键在于GuardedObject</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">GuardedObject</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> Object response;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">Object</span> <span class="variable">lock</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">get</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (lock) &#123;</span><br><span class="line">            <span class="comment">// 等待条件</span></span><br><span class="line">            <span class="keyword">while</span> (response == <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    lock.wait();</span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">return</span> response;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">complete</span><span class="params">(Object response)</span> &#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (lock) &#123;</span><br><span class="line">            <span class="comment">// 条件满足，通知等待线程</span></span><br><span class="line">            <span class="built_in">this</span>.response = response;</span><br><span class="line">            lock.notifyAll();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>应用：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="type">GuardedObject</span> <span class="variable">guardedObject</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">GuardedObject</span>();</span><br><span class="line">    <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 子线程执行下载</span></span><br><span class="line">            List&lt;String&gt; response = download();</span><br><span class="line">            log.debug(<span class="string">&quot;download complete...&quot;</span>);</span><br><span class="line">            guardedObject.complete(response);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;).start();</span><br><span class="line">    log.debug(<span class="string">&quot;waiting...&quot;</span>);</span><br><span class="line">    <span class="comment">// 主线程阻塞等待</span></span><br><span class="line">    <span class="type">Object</span> <span class="variable">response</span> <span class="operator">=</span> guardedObject.get();</span><br><span class="line">    log.debug(<span class="string">&quot;get response: [&#123;&#125;] lines&quot;</span>, ((List&lt;String&gt;) response).size());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="扩展"><a href="#扩展" class="headerlink" title="扩展"></a>扩展</h4><p>扩展：增加超时效果</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//timeout表示等多久</span></span><br><span class="line"><span class="keyword">public</span> Object <span class="title function_">get</span><span class="params">(<span class="type">long</span> timeout)</span> &#123;</span><br><span class="line">    <span class="keyword">synchronized</span> (lock) &#123;</span><br><span class="line">        <span class="comment">// 记录一个开始等待的时间</span></span><br><span class="line">        <span class="type">long</span> <span class="variable">begin</span> <span class="operator">=</span> System.currnetTimeMills();</span><br><span class="line">        <span class="type">long</span> <span class="variable">passedTime</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">while</span> (response == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="comment">//这一轮应该等待的时间</span></span><br><span class="line">            <span class="type">long</span> <span class="variable">waitTime</span> <span class="operator">=</span> timeout-passedTime;</span><br><span class="line">            <span class="keyword">if</span>(waitTime &lt;= <span class="number">0</span>)&#123;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                lock.wait(waitTime);<span class="comment">//等待的时间会随着唤醒的时刻变化</span></span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">            passedTime = System.currnetTimeMills()-begin;<span class="comment">//经历的时间</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> response;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>扩展：多任务版 GuardedObject</p>
<p>图中 Futures 就好比居民楼一层的信箱（每个信箱有房间编号），左侧的 t0，t2，t4 就好比等待邮件的居民，右侧的 t1，t3，t5 就好比邮递员，如果需要在多个类之间使用 GuardedObject 对象，作为参数传递不是很方便，因此设计一个用来解耦的中间类， 这样不仅能够解耦【结果等待者】和【结果生产者】，还能够同时支持多个任务的管理</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220413153953496.png" alt="image-20220413153953496"></p>
<p>代码实现</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">GuardedObject</span> &#123;</span><br><span class="line">    <span class="comment">// 新增 id 用来标识 Guarded Object</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> id;</span><br><span class="line">    <span class="comment">//同上</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//中间解耦类</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Mailboxes</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> Map&lt;Integer, GuardedObject&gt; boxes = <span class="keyword">new</span> <span class="title class_">Hashtable</span>&lt;&gt;();</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">int</span> <span class="variable">id</span> <span class="operator">=</span> <span class="number">1</span>;</span><br><span class="line">    <span class="comment">// 产生唯一 id</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">synchronized</span> <span class="type">int</span> <span class="title function_">generateId</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> id++;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> GuardedObject <span class="title function_">getGuardedObject</span><span class="params">(<span class="type">int</span> id)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> boxes.remove(id);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> GuardedObject <span class="title function_">createGuardedObject</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">GuardedObject</span> <span class="variable">go</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">GuardedObject</span>(generateId());</span><br><span class="line">        boxes.put(go.getId(), go);</span><br><span class="line">        <span class="keyword">return</span> go;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> Set&lt;Integer&gt; <span class="title function_">getIds</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> boxes.keySet();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//生产者</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Postman</span> <span class="keyword">extends</span> <span class="title class_">Thread</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> mailId;</span><br><span class="line">    <span class="keyword">private</span> String mail;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Postman</span><span class="params">(<span class="type">int</span> mailId, String mail)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.mailId = mailId;</span><br><span class="line">        <span class="built_in">this</span>.mail = mail;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">GuardedObject</span> <span class="variable">guardedObject</span> <span class="operator">=</span> Mailboxes.getGuardedObject(mailId);</span><br><span class="line">        log.debug(<span class="string">&quot;送信 id:&#123;&#125;, 内容:&#123;&#125;&quot;</span>, id, mail);</span><br><span class="line">        guardedObject.complete(mail);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//消费者</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">People</span> <span class="keyword">extends</span> <span class="title class_">Thread</span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="comment">// 收信</span></span><br><span class="line">        <span class="type">GuardedObject</span> <span class="variable">guardedObject</span> <span class="operator">=</span> Mailboxes.createGuardedObject();</span><br><span class="line">        log.debug(<span class="string">&quot;开始收信 id:&#123;&#125;&quot;</span>, guardedObject.getId());</span><br><span class="line">        <span class="type">Object</span> <span class="variable">mail</span> <span class="operator">=</span> guardedObject.get(<span class="number">5000</span>);</span><br><span class="line">        log.debug(<span class="string">&quot;收到信 id:&#123;&#125;, 内容:&#123;&#125;&quot;</span>, guardedObject.getId(), mail);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//测试</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; <span class="number">3</span>; i++) &#123;</span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">People</span>().start();</span><br><span class="line">    &#125;</span><br><span class="line">    Sleeper.sleep(<span class="number">1</span>);</span><br><span class="line">    <span class="keyword">for</span> (Integer id : Mailboxes.getIds()) &#123;</span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Postman</span>(id, <span class="string">&quot;内容&quot;</span> + id).start();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="join原理"><a href="#join原理" class="headerlink" title="join原理"></a>join原理</h4><p>是调用者轮询检查线程 alive 状态</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title function_">join</span><span class="params">(<span class="type">long</span> millis)</span></span><br><span class="line">    <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">    <span class="type">long</span> <span class="variable">base</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">    <span class="type">long</span> <span class="variable">now</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">if</span> (millis &lt; <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">IllegalArgumentException</span>(<span class="string">&quot;timeout value is negative&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (millis == <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="comment">// 调用者线程进入waitSet 等待, 直到运行结束</span></span><br><span class="line">        <span class="keyword">while</span> (isAlive()) &#123;</span><br><span class="line">            wait(<span class="number">0</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">while</span> (isAlive()) &#123;</span><br><span class="line">            <span class="type">long</span> <span class="variable">delay</span> <span class="operator">=</span> millis - now;</span><br><span class="line">            <span class="keyword">if</span> (delay &lt;= <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            wait(delay);</span><br><span class="line">            now = System.currentTimeMillis() - base;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>join 体现的是【保护性暂停】模式</p>
<p>注意：wait()的作用是让“当前线程”等待，而这里的“当前线程”是指当前运行的线程。虽然是调用子线程的wait()方法，但是它是通过“主线程”去调用的；所以，休眠的是主线程，而不是“子线程”！ ，isAlive()判断子线程是否存活</p>
</blockquote>
<h3 id="异步模式之生产者消费者"><a href="#异步模式之生产者消费者" class="headerlink" title="异步模式之生产者消费者"></a>异步模式之生产者消费者</h3><p>基于wait notify的应用</p>
<h4 id="定义-1"><a href="#定义-1" class="headerlink" title="定义"></a>定义</h4><ul>
<li>与前面的保护性暂停中的 GuardObject 不同，不需要产生结果和消费结果的线程一一对应</li>
<li>消费队列可以用来平衡生产和消费的线程资源</li>
<li>生产者仅负责产生结果数据，不关心数据该如何处理，而消费者专心处理结果数据</li>
<li>消息队列是有容量限制的，满时不会再加入数据，空时不会再消耗数据</li>
<li>JDK 中各种阻塞队列，采用的就是这种模式</li>
</ul>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220413160953761.png" alt="image-20220413160953761"></p>
<h4 id="实现-1"><a href="#实现-1" class="headerlink" title="实现"></a>实现</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Message</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> id;</span><br><span class="line">    <span class="keyword">private</span> Object message;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Message</span><span class="params">(<span class="type">int</span> id, Object message)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.id = id;</span><br><span class="line">        <span class="built_in">this</span>.message = message;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getId</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> id;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> Object <span class="title function_">getMessage</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> message;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//消息队列，java线程之间通信</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MessageQueue</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> LinkedList&lt;Message&gt; queue;<span class="comment">//存放消息的队列集合</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> capacity;<span class="comment">//容量上限</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">MessageQueue</span><span class="params">(<span class="type">int</span> capacity)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.capacity = capacity;</span><br><span class="line">        queue = <span class="keyword">new</span> <span class="title class_">LinkedList</span>&lt;&gt;();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> Message <span class="title function_">take</span><span class="params">()</span> &#123;<span class="comment">//获取消息</span></span><br><span class="line">        <span class="keyword">synchronized</span> (queue) &#123;</span><br><span class="line">            <span class="keyword">while</span> (queue.isEmpty()) &#123;</span><br><span class="line">                log.debug(<span class="string">&quot;没货了, wait&quot;</span>);</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    queue.wait();</span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="type">Message</span> <span class="variable">message</span> <span class="operator">=</span> queue.removeFirst();</span><br><span class="line">            queue.notifyAll();<span class="comment">//互相唤醒</span></span><br><span class="line">            <span class="keyword">return</span> message;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">put</span><span class="params">(Message message)</span> &#123;<span class="comment">//存入消息</span></span><br><span class="line">        <span class="keyword">synchronized</span> (queue) &#123;</span><br><span class="line">            <span class="keyword">while</span> (queue.size() == capacity) &#123;</span><br><span class="line">                log.debug(<span class="string">&quot;库存已达上限, wait&quot;</span>);</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    queue.wait();</span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            queue.addLast(message);</span><br><span class="line">            queue.notifyAll();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="同步模式之顺序控制"><a href="#同步模式之顺序控制" class="headerlink" title="同步模式之顺序控制"></a>同步模式之顺序控制</h3><h4 id="固定运行顺序"><a href="#固定运行顺序" class="headerlink" title="固定运行顺序"></a>固定运行顺序</h4><p>比如，必须先 2 后 1 打印</p>
<h5 id="wait-notify-版"><a href="#wait-notify-版" class="headerlink" title="wait-notify 版"></a>wait-notify 版</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 用来同步的对象</span></span><br><span class="line"><span class="keyword">static</span> <span class="type">Object</span> <span class="variable">obj</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line"><span class="comment">// t2 运行标记， 代表 t2 是否执行过</span></span><br><span class="line"><span class="keyword">static</span> <span class="type">boolean</span> <span class="variable">t2runed</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">    <span class="type">Thread</span> <span class="variable">t1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (obj) &#123;</span><br><span class="line">            <span class="comment">// 如果 t2 没有执行过</span></span><br><span class="line">            <span class="keyword">while</span> (!t2runed) &#123; </span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="comment">// t1 先等一会</span></span><br><span class="line">                    obj.wait(); </span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(<span class="number">1</span>);</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="type">Thread</span> <span class="variable">t2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">        System.out.println(<span class="number">2</span>);</span><br><span class="line">        <span class="keyword">synchronized</span> (obj) &#123;</span><br><span class="line">            <span class="comment">// 修改运行标记</span></span><br><span class="line">            t2runed = <span class="literal">true</span>;</span><br><span class="line">            <span class="comment">// 通知 obj 上等待的线程（可能有多个，因此需要用 notifyAll）</span></span><br><span class="line">            obj.notifyAll();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">    t1.start();</span><br><span class="line">    t2.start();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h5 id="Park-Unpark-版"><a href="#Park-Unpark-版" class="headerlink" title="Park Unpark 版"></a>Park Unpark 版</h5><p>上面的实现上很麻烦： </p>
<ul>
<li>首先，需要保证先 wait 再 notify，否则 wait 线程永远得不到唤醒。因此使用了『运行标记』来判断该不该 wait </li>
<li>第二，如果有些干扰线程错误地 notify 了 wait 线程，条件不满足时还要重新等待，使用了 while 循环来解决 此问题 </li>
<li>最后，唤醒对象上的 wait 线程需要使用 notifyAll，因为『同步对象』上的等待线程可能不止一个 </li>
</ul>
<p>可以使用 LockSupport 类的 park 和 unpark 来简化上面的题目：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">Thread</span> <span class="variable">t1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">    <span class="keyword">try</span> &#123; Thread.sleep(<span class="number">1000</span>); &#125; <span class="keyword">catch</span> (InterruptedException e) &#123; &#125;</span><br><span class="line">    <span class="comment">// 当没有『许可』时，当前线程暂停运行；有『许可』时，用掉这个『许可』，当前线程恢复运行</span></span><br><span class="line">    LockSupport.park();</span><br><span class="line">    System.out.println(<span class="string">&quot;1&quot;</span>);</span><br><span class="line">&#125;);</span><br><span class="line"><span class="type">Thread</span> <span class="variable">t2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">    System.out.println(<span class="string">&quot;2&quot;</span>);</span><br><span class="line">    <span class="comment">// 给线程 t1 发放『许可』（多次连续调用 unpark 只会发放一个『许可』）</span></span><br><span class="line">    LockSupport.unpark(t1);</span><br><span class="line">&#125;);</span><br><span class="line">t1.start();</span><br><span class="line">t2.start();</span><br></pre></td></tr></table></figure>

<p>park 和 unpark 方法比较灵活，他俩谁先调用，谁后调用无所谓。并且是以线程为单位进行『暂停』和『恢复』， 不需要『同步对象』和『运行标记』</p>
<h4 id="交替输出"><a href="#交替输出" class="headerlink" title="交替输出"></a>交替输出</h4><p>线程 1 输出 a 5 次，线程 2 输出 b 5 次，线程 3 输出 c 5 次。现在要求输出 abcabcabcabcabc 怎么实现</p>
<h5 id="wait-notify-版-1"><a href="#wait-notify-版-1" class="headerlink" title="wait notify 版"></a>wait notify 版</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">SyncWaitNotify</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> flag;<span class="comment">//等待的标记</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> loopNumber;<span class="comment">//循环的次数</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">SyncWaitNotify</span><span class="params">(<span class="type">int</span> flag, <span class="type">int</span> loopNumber)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.flag = flag;</span><br><span class="line">        <span class="built_in">this</span>.loopNumber = loopNumber;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">print</span><span class="params">(<span class="type">int</span> waitFlag, <span class="type">int</span> nextFlag, String str)</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; loopNumber; i++) &#123;</span><br><span class="line">            <span class="keyword">synchronized</span> (<span class="built_in">this</span>) &#123;</span><br><span class="line">                <span class="keyword">while</span> (<span class="built_in">this</span>.flag != waitFlag) &#123;</span><br><span class="line">                    <span class="keyword">try</span> &#123;</span><br><span class="line">                        <span class="built_in">this</span>.wait();</span><br><span class="line">                    &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                        e.printStackTrace();</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">                System.out.print(str);</span><br><span class="line">                flag = nextFlag;</span><br><span class="line">                <span class="built_in">this</span>.notifyAll();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">SyncWaitNotify</span> <span class="variable">syncWaitNotify</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SyncWaitNotify</span>(<span class="number">1</span>, <span class="number">5</span>);</span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">    syncWaitNotify.print(<span class="number">1</span>, <span class="number">2</span>, <span class="string">&quot;a&quot;</span>);</span><br><span class="line">&#125;).start();</span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">    syncWaitNotify.print(<span class="number">2</span>, <span class="number">3</span>, <span class="string">&quot;b&quot;</span>);</span><br><span class="line">&#125;).start();</span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">    syncWaitNotify.print(<span class="number">3</span>, <span class="number">1</span>, <span class="string">&quot;c&quot;</span>);</span><br><span class="line">&#125;).start();</span><br></pre></td></tr></table></figure>

<h5 id="Lock-条件变量版"><a href="#Lock-条件变量版" class="headerlink" title="Lock 条件变量版"></a>Lock 条件变量版</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">AwaitSignal</span> <span class="keyword">extends</span> <span class="title class_">ReentrantLock</span> &#123;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">start</span><span class="params">(Condition first)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.lock();</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            log.debug(<span class="string">&quot;start&quot;</span>);</span><br><span class="line">            first.signal();</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            <span class="built_in">this</span>.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">print</span><span class="params">(String str, Condition current, Condition next)</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; loopNumber; i++) &#123;</span><br><span class="line">            <span class="built_in">this</span>.lock();</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                current.await();</span><br><span class="line">                log.debug(str);</span><br><span class="line">                next.signal();</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                <span class="built_in">this</span>.unlock();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 循环次数</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> loopNumber;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">AwaitSignal</span><span class="params">(<span class="type">int</span> loopNumber)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.loopNumber = loopNumber;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="type">AwaitSignal</span> <span class="variable">as</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AwaitSignal</span>(<span class="number">5</span>);</span><br><span class="line"><span class="type">Condition</span> <span class="variable">aWaitSet</span> <span class="operator">=</span> as.newCondition();</span><br><span class="line"><span class="type">Condition</span> <span class="variable">bWaitSet</span> <span class="operator">=</span> as.newCondition();</span><br><span class="line"><span class="type">Condition</span> <span class="variable">cWaitSet</span> <span class="operator">=</span> as.newCondition();</span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">    as.print(<span class="string">&quot;a&quot;</span>, aWaitSet, bWaitSet);</span><br><span class="line">&#125;).start();</span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">    as.print(<span class="string">&quot;b&quot;</span>, bWaitSet, cWaitSet);</span><br><span class="line">&#125;).start();</span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">    as.print(<span class="string">&quot;c&quot;</span>, cWaitSet, aWaitSet);</span><br><span class="line">&#125;).start();</span><br><span class="line">as.start(aWaitSet);<span class="comment">//为了防止死锁，得先唤醒一个</span></span><br></pre></td></tr></table></figure>

<h5 id="Park-Unpark-版-1"><a href="#Park-Unpark-版-1" class="headerlink" title="Park Unpark 版"></a>Park Unpark 版</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">ParkUnpark</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> loopNumber;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">SyncPark</span><span class="params">(<span class="type">int</span> loopNumber)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.loopNumber = loopNumber;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">print</span><span class="params">(String str,Thread next)</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; loopNumber; i++) &#123;</span><br><span class="line">            LockSupport.park();</span><br><span class="line">            System.out.print(str);</span><br><span class="line">            LockSupport.unpark(next);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Test</span>&#123;</span><br><span class="line">    <span class="keyword">static</span> Thread t1;</span><br><span class="line">    <span class="keyword">static</span> Thread t1;</span><br><span class="line">    <span class="keyword">static</span> Thread t1;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span>&#123;</span><br><span class="line">        <span class="type">SyncPark</span> <span class="variable">syncPark</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">SyncPark</span>(<span class="number">5</span>);</span><br><span class="line">        <span class="type">Thread</span> <span class="variable">t1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">            syncPark.print(<span class="string">&quot;a&quot;</span>);</span><br><span class="line">        &#125;);</span><br><span class="line">        <span class="type">Thread</span> <span class="variable">t2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">            syncPark.print(<span class="string">&quot;b&quot;</span>);</span><br><span class="line">        &#125;);</span><br><span class="line">        <span class="type">Thread</span> <span class="variable">t3</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">            syncPark.print(<span class="string">&quot;c\n&quot;</span>);</span><br><span class="line">        &#125;);</span><br><span class="line">		t1.start();</span><br><span class="line">        t2.start();</span><br><span class="line">        t3.start();</span><br><span class="line">        LockSupport.unpark(t1);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="两阶段终止模式"><a href="#两阶段终止模式" class="headerlink" title="两阶段终止模式"></a>两阶段终止模式</h3><p>interrupt方法的应用，也可以用volatile</p>
<p>当我们在执行线程一时，想要终止线程二，这是就需要使用interrupt方法来优雅的停止线程二。</p>
<p>例如如下场景：有一个监控线程</p>
<p><img src="https://zqyhdm-blog.oss-cn-shanghai.aliyuncs.com/markdown/image-20220407152321768.png" alt="image-20220407152321768"></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">TwoPhaseTermination</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> Thread monitor;</span><br><span class="line">    <span class="comment">//启动监控器线程</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">start</span><span class="params">()</span> &#123;</span><br><span class="line">        monitor = <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">            <span class="keyword">while</span> (<span class="literal">true</span>)&#123;</span><br><span class="line">                <span class="keyword">if</span>(Thread.currentThread().isInterrupted())&#123;</span><br><span class="line">                    System.out.println(<span class="string">&quot;料理后事&quot;</span>);</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    Thread.sleep(<span class="number">1000</span>);<span class="comment">//睡眠时也可能被打断</span></span><br><span class="line">                    System.out.println(<span class="string">&quot;执行监控任务&quot;</span>);</span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                    Thread.currentThread().interrupt();<span class="comment">//重新设置打断标记</span></span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">        monitor.start();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//用于停止监控器线程</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">stop</span><span class="params">()</span> &#123;</span><br><span class="line">        monitor.interrupt();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>监控的线程在运行时可以直接打断，设置打断标记；监控线程睡眠时，打断不会设置打断标记，但是会抛出异常，因此需要捕捉异常并再次设置打断标记。</p>
<h3 id="同步模式之-Balking"><a href="#同步模式之-Balking" class="headerlink" title="同步模式之 Balking"></a>同步模式之 Balking</h3><p>Balking （犹豫）模式用在一个线程发现另一个线程或本线程已经做了某一件相同的事，那么本线程就无需再做了，直接结束返回</p>
<p>例如：当前端页面多次点击按钮调用 start 时</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MonitorService</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> Thread monitor;</span><br><span class="line">    <span class="comment">// 用来表示是否已经有线程已经在执行启动了</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">volatile</span> <span class="type">boolean</span> starting;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">start</span><span class="params">()</span> &#123;</span><br><span class="line">        log.info(<span class="string">&quot;尝试启动监控线程...&quot;</span>);</span><br><span class="line">        <span class="keyword">synchronized</span> (<span class="built_in">this</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (starting) &#123;</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            starting = <span class="literal">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 真正启动监控线程...</span></span><br><span class="line">        monitor = <span class="keyword">new</span> <span class="title class_">Thread</span>(...);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>它还经常用来实现线程安全的单例</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">Singleton</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="title function_">Singleton</span><span class="params">()</span> &#123;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="type">Singleton</span> <span class="variable">INSTANCE</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">synchronized</span> Singleton <span class="title function_">getInstance</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (INSTANCE != <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> INSTANCE;</span><br><span class="line">        &#125;</span><br><span class="line">        INSTANCE = <span class="keyword">new</span> <span class="title class_">Singleton</span>();</span><br><span class="line">        <span class="keyword">return</span> INSTANCE;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="模式之享元"><a href="#模式之享元" class="headerlink" title="模式之享元"></a>模式之享元</h3><p>保护性拷贝的扩展，实现对象的重用</p>
<p>定义英文名称：Flyweight pattern. 当需要重用数量有限的同一类对象时</p>
<h4 id="包装类"><a href="#包装类" class="headerlink" title="包装类"></a>包装类</h4><p>在JDK中 Boolean，Byte，Short，Integer，Long，Character 等包装类提供了 valueOf 方法，例如 Long 的 valueOf 会缓存 -128~127 之间的 Long 对象，在这个范围之间会重用对象，大于这个范围，才会新建 Long 对 象：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> Long <span class="title function_">valueOf</span><span class="params">(<span class="type">long</span> l)</span> &#123;</span><br><span class="line">    <span class="keyword">final</span> <span class="type">int</span> <span class="variable">offset</span> <span class="operator">=</span> <span class="number">128</span>;</span><br><span class="line">    <span class="keyword">if</span> (l &gt;= -<span class="number">128</span> &amp;&amp; l &lt;= <span class="number">127</span>) &#123; <span class="comment">// will cache</span></span><br><span class="line">        <span class="keyword">return</span> LongCache.cache[(<span class="type">int</span>)l + offset];</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Long</span>(l);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<blockquote>
<p>注意：</p>
<ul>
<li>Byte, Short, Long 缓存的范围都是 -128~127 </li>
<li>Character 缓存的范围是 0~127 </li>
<li>Integer的默认范围是 -128~127 最小值不能变 但最大值可以通过调整虚拟机参数 <code>  -Djava.lang.Integer.IntegerCache.high</code> 来改变</li>
<li>Boolean 缓存了 TRUE 和 FALSE</li>
</ul>
</blockquote>
<h4 id="String-串池"><a href="#String-串池" class="headerlink" title="String 串池"></a>String 串池</h4><h4 id="应用-3"><a href="#应用-3" class="headerlink" title="应用"></a>应用</h4><p>例如：一个线上商城应用，QPS 达到数千，如果每次都重新创建和关闭数据库连接，性能会受到极大影响。 这时 预先创建好一批连接，放入连接池。一次请求到达后，从连接池获取连接，使用完毕后再还回连接池，这样既节约 了连接的创建和关闭时间，也实现了连接的重用，不至于让庞大的连接数压垮数据库。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Pool</span> &#123;</span><br><span class="line">    <span class="comment">// 1. 连接池大小</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">int</span> poolSize;</span><br><span class="line">    <span class="comment">// 2. 连接对象数组</span></span><br><span class="line">    <span class="keyword">private</span> Connection[] connections;</span><br><span class="line">    <span class="comment">// 3. 连接状态数组 0 表示空闲， 1 表示繁忙</span></span><br><span class="line">    <span class="keyword">private</span> AtomicIntegerArray states;</span><br><span class="line">    <span class="comment">// 4. 构造方法初始化</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">Pool</span><span class="params">(<span class="type">int</span> poolSize)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.poolSize = poolSize;</span><br><span class="line">        <span class="built_in">this</span>.connections = <span class="keyword">new</span> <span class="title class_">Connection</span>[poolSize];</span><br><span class="line">        <span class="built_in">this</span>.states = <span class="keyword">new</span> <span class="title class_">AtomicIntegerArray</span>(<span class="keyword">new</span> <span class="title class_">int</span>[poolSize]);</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; poolSize; i++) &#123;</span><br><span class="line">            connections[i] = <span class="keyword">new</span> <span class="title class_">MockConnection</span>(<span class="string">&quot;连接&quot;</span> + (i+<span class="number">1</span>));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 5. 借连接</span></span><br><span class="line">    <span class="keyword">public</span> Connection <span class="title function_">borrow</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">while</span>(<span class="literal">true</span>) &#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; poolSize; i++) &#123;</span><br><span class="line">                <span class="comment">// 获取空闲连接</span></span><br><span class="line">                <span class="keyword">if</span>(states.get(i) == <span class="number">0</span>) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (states.compareAndSet(i, <span class="number">0</span>, <span class="number">1</span>)) &#123;</span><br><span class="line">                        log.debug(<span class="string">&quot;borrow &#123;&#125;&quot;</span>, connections[i]);</span><br><span class="line">                        <span class="keyword">return</span> connections[i];</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// 如果没有空闲连接，当前线程进入等待</span></span><br><span class="line">            <span class="keyword">synchronized</span> (<span class="built_in">this</span>) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    log.debug(<span class="string">&quot;wait...&quot;</span>);</span><br><span class="line">                    <span class="built_in">this</span>.wait();</span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 6. 归还连接</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">free</span><span class="params">(Connection conn)</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; poolSize; i++) &#123;</span><br><span class="line">            <span class="keyword">if</span> (connections[i] == conn) &#123;</span><br><span class="line">                states.set(i, <span class="number">0</span>);</span><br><span class="line">                <span class="keyword">synchronized</span> (<span class="built_in">this</span>) &#123;</span><br><span class="line">                    log.debug(<span class="string">&quot;free &#123;&#125;&quot;</span>, conn);</span><br><span class="line">                    <span class="built_in">this</span>.notifyAll();</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MockConnection</span> <span class="keyword">implements</span> <span class="title class_">Connection</span> &#123;</span><br><span class="line">    <span class="comment">// 实现略</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>以上实现没有考虑： </p>
<ul>
<li>连接的动态增长与收缩</li>
<li>连接保活（可用性检测）</li>
<li>等待超时处理</li>
<li>分布式 hash </li>
</ul>
<p>对于关系型数据库，有比较成熟的连接池实现，例如c3p0, druid等，对于更通用的对象池，可以考虑使用apache commons pool，例如redis连接池可以参考jedis中关于连接池的实现</p>
<h3 id="异步模式之工作线程"><a href="#异步模式之工作线程" class="headerlink" title="异步模式之工作线程"></a>异步模式之工作线程</h3><p>让有限的工作线程（Worker Thread）来轮流异步处理无限多的任务。也可以将其归类为分工模式，它的典型实现 就是线程池，也体现了经典设计模式中的享元模式。</p>
<p>注意，不同任务类型应该使用不同的线程池，这样能够避免饥饿，并能提升效率</p>
<h4 id="饥饿问题"><a href="#饥饿问题" class="headerlink" title="饥饿问题"></a>饥饿问题</h4><p>固定大小线程池会有饥饿现象</p>
<p>两个工人是同一个线程池中的两个线程 他们要做的事情是：为客人点餐和到后厨做菜，这是两个阶段的工作，比如工人A 处理了点餐任务，接下来它要等着 工人B 把菜做好，然后上菜，但现在同时来了两个客人，这个时候工人A 和工人B 都去处理点餐了，这时没人做饭了，饥饿</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TestDeadLock</span> &#123;</span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> List&lt;String&gt; MENU = Arrays.asList(<span class="string">&quot;地三鲜&quot;</span>, <span class="string">&quot;宫保鸡丁&quot;</span>, <span class="string">&quot;辣子鸡丁&quot;</span>, <span class="string">&quot;烤鸡翅&quot;</span>);</span><br><span class="line">    <span class="keyword">static</span> <span class="type">Random</span> <span class="variable">RANDOM</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Random</span>();</span><br><span class="line">    <span class="keyword">static</span> String <span class="title function_">cooking</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> MENU.get(RANDOM.nextInt(MENU.size()));</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">ExecutorService</span> <span class="variable">executorService</span> <span class="operator">=</span> Executors.newFixedThreadPool(<span class="number">2</span>);</span><br><span class="line">        executorService.execute(() -&gt; &#123;</span><br><span class="line">            log.debug(<span class="string">&quot;处理点餐...&quot;</span>);</span><br><span class="line">            Future&lt;String&gt; f = executorService.submit(() -&gt; &#123;</span><br><span class="line">                log.debug(<span class="string">&quot;做菜&quot;</span>);</span><br><span class="line">                <span class="keyword">return</span> cooking();</span><br><span class="line">            &#125;);</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                log.debug(<span class="string">&quot;上菜: &#123;&#125;&quot;</span>, f.get());</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException | ExecutionException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">        <span class="type">ExecutorService</span> <span class="variable">executorService</span> <span class="operator">=</span> Executors.newFixedThreadPool(<span class="number">2</span>);</span><br><span class="line">        executorService.execute(() -&gt; &#123;</span><br><span class="line">            log.debug(<span class="string">&quot;处理点餐...&quot;</span>);</span><br><span class="line">            Future&lt;String&gt; f = executorService.submit(() -&gt; &#123;</span><br><span class="line">                log.debug(<span class="string">&quot;做菜&quot;</span>);</span><br><span class="line">                <span class="keyword">return</span> cooking();</span><br><span class="line">            &#125;);</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                log.debug(<span class="string">&quot;上菜: &#123;&#125;&quot;</span>, f.get());</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException | ExecutionException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>上面会出现线程饥饿问题</p>
<p>解决方法可以增加线程池的大小，不过不是根本解决方案，还是前面提到的，不同的任务类型，采用不同的线程池，例如：</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TestDeadLock</span> &#123;</span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> List&lt;String&gt; MENU = Arrays.asList(<span class="string">&quot;地三鲜&quot;</span>, <span class="string">&quot;宫保鸡丁&quot;</span>, <span class="string">&quot;辣子鸡丁&quot;</span>, <span class="string">&quot;烤鸡翅&quot;</span>);</span><br><span class="line">    <span class="keyword">static</span> <span class="type">Random</span> <span class="variable">RANDOM</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Random</span>();</span><br><span class="line">    <span class="keyword">static</span> String <span class="title function_">cooking</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> MENU.get(RANDOM.nextInt(MENU.size()));</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">ExecutorService</span> <span class="variable">waiterPool</span> <span class="operator">=</span> Executors.newFixedThreadPool(<span class="number">1</span>);</span><br><span class="line">        <span class="type">ExecutorService</span> <span class="variable">cookPool</span> <span class="operator">=</span> Executors.newFixedThreadPool(<span class="number">1</span>);</span><br><span class="line">        waiterPool.execute(() -&gt; &#123;</span><br><span class="line">            log.debug(<span class="string">&quot;处理点餐...&quot;</span>);</span><br><span class="line">            Future&lt;String&gt; f = cookPool.submit(() -&gt; &#123;</span><br><span class="line">                log.debug(<span class="string">&quot;做菜&quot;</span>);</span><br><span class="line">                <span class="keyword">return</span> cooking();</span><br><span class="line">            &#125;);</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                log.debug(<span class="string">&quot;上菜: &#123;&#125;&quot;</span>, f.get());</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException | ExecutionException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">        waiterPool.execute(() -&gt; &#123;</span><br><span class="line">            log.debug(<span class="string">&quot;处理点餐...&quot;</span>);</span><br><span class="line">            Future&lt;String&gt; f = cookPool.submit(() -&gt; &#123;</span><br><span class="line">                log.debug(<span class="string">&quot;做菜&quot;</span>);</span><br><span class="line">                <span class="keyword">return</span> cooking();</span><br><span class="line">            &#125;);</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                log.debug(<span class="string">&quot;上菜: &#123;&#125;&quot;</span>, f.get());</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException | ExecutionException e) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h4 id="创建多少线程池合适"><a href="#创建多少线程池合适" class="headerlink" title="创建多少线程池合适"></a>创建多少线程池合适</h4><ul>
<li>过小会导致程序不能充分地利用系统资源、容易导致饥饿</li>
<li>过大会导致更多的线程上下文切换，占用更多内存</li>
</ul>
<ol>
<li><p>CPU 密集型运算</p>
<p>通常采用 cpu 核数 + 1 能够实现最优的 CPU 利用率，+1 是保证当线程由于页缺失故障（操作系统）或其它原因导致暂停时，额外的这个线程就能顶上去，保证 CPU 时钟周期不被浪费</p>
</li>
<li><p>I/O 密集型运算</p>
<p>CPU 不总是处于繁忙状态，例如，当你执行业务计算时，这时候会使用 CPU 资源，但当执行 I/O 操作时、远程RPC 调用时，包括进行数据库操作时，这时候 CPU 就闲下来了，可以利用多线程提高它的利用率。</p>
<p>经验公式如下</p>
<p>线程数 = 核数 * 期望 CPU 利用率 * 总时间(CPU计算时间+等待时间) / CPU 计算时间</p>
<p>例如 4 核 CPU 计算时间是 50% ，其它等待时间是 50%，期望 cpu 被 100% 利用，套用公式</p>
<p>4 * 100% * 100% / 50% = 8</p>
</li>
</ol>

      
    </div>
    <div class="article-footer">
      <blockquote class="mt-2x">
  <ul class="post-copyright list-unstyled">
    
    <li class="post-copyright-link hidden-xs">
      <strong>本文链接：</strong>
      <a href="https://skyung.gitee.io/Skyung/2022/04/20/java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/" title="JVM并发编程" target="_blank" rel="external">https://skyung.gitee.io/Skyung/2022/04/20/java并发编程/</a>
    </li>
    
    <li class="post-copyright-license">
      <strong>版权声明： </strong> 本博客所有文章除特别声明外，均采用 <a href="http://creativecommons.org/licenses/by/4.0/deed.zh" target="_blank" rel="external">CC BY 4.0 CN协议</a> 许可协议。转载请注明出处！
    </li>
  </ul>
</blockquote>


<div class="panel panel-default panel-badger">
  <div class="panel-body">
    <figure class="media">
      <div class="media-left">
        <a href="https://github.com/cofess" target="_blank" class="img-burn thumb-sm visible-lg">
          <img src="/Skyung/images/avatar.jpg" class="img-rounded w-full" alt="">
        </a>
      </div>
      <div class="media-body">
        <h3 class="media-heading"><a href="https://github.com/cofess" target="_blank"><span class="text-dark">Skyung</span><small class="ml-1x">Web Developer</small></a></h3>
        <div>脚踏实地，仰望星空</div>
      </div>
    </figure>
  </div>
</div>


    </div>
  </article>
  
    
  <section id="comments">
  	
      <div id="uyan_frame"></div>
    
  </section>


  
</div>

  <nav class="bar bar-footer clearfix" data-stick-bottom>
  <div class="bar-inner">
  
  <ul class="pager pull-left">
    
    <li class="prev">
      <a href="/Skyung/2022/04/20/java%E7%9A%84ThreadLocal%E7%B1%BB/" title="ThreadLocal"><i class="icon icon-angle-left" aria-hidden="true"></i><span>&nbsp;&nbsp;上一篇</span></a>
    </li>
    
    
    <li class="next">
      <a href="/Skyung/2022/04/20/netty/" title="Netty"><span>下一篇&nbsp;&nbsp;</span><i class="icon icon-angle-right" aria-hidden="true"></i></a>
    </li>
    
    
    <li class="toggle-toc">
      <a class="toggle-btn " data-toggle="collapse" href="#collapseToc" aria-expanded="false" title="文章目录" role="button">    <span>[&nbsp;</span><span>文章目录</span>
        <i class="text-collapsed icon icon-anchor"></i>
        <i class="text-in icon icon-close"></i>
        <span>]</span>
      </a>
    </li>
    
  </ul>
  
  
  <!-- Button trigger modal -->
  <button type="button" class="btn btn-fancy btn-donate pop-onhover bg-gradient-warning" data-toggle="modal" data-target="#donateModal"><span>赏</span></button>
  <!-- <div class="wave-icon wave-icon-danger btn-donate" data-toggle="modal" data-target="#donateModal">
    <div class="wave-circle"><span class="icon"><i class="icon icon-bill"></i></span></div>
  </div> -->
  
  
  <div class="bar-right">
    
    <div class="share-component" data-sites="weibo,qq,wechat,facebook,twitter" data-mobile-sites="weibo,qq,qzone"></div>
    
  </div>
  </div>
</nav>
  
<!-- Modal -->
<div class="modal modal-center modal-small modal-xs-full fade" id="donateModal" tabindex="-1" role="dialog">
  <div class="modal-dialog" role="document">
    <div class="modal-content donate">
      <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
      <div class="modal-body">
        <div class="donate-box">
          <div class="donate-head">
            <p>感谢您的支持，我会继续努力的!</p>
          </div>
          <div class="tab-content">
            <div role="tabpanel" class="tab-pane fade active in" id="alipay">
              <div class="donate-payimg">
                <img src="/Skyung/images/donate/alipay.jpg" alt="扫码支持" title="扫一扫" />
              </div>
              <p class="text-muted mv">扫码打赏，你说多少就多少</p>
              <p class="text-grey">打开支付宝扫一扫，即可进行扫码打赏哦</p>
            </div>
            <div role="tabpanel" class="tab-pane fade" id="wechatpay">
              <div class="donate-payimg">
                <img src="/Skyung/images/donate/wechat.jpg" alt="扫码支持" title="扫一扫" />
              </div>
              <p class="text-muted mv">扫码打赏，你说多少就多少</p>
              <p class="text-grey">打开微信扫一扫，即可进行扫码打赏哦</p>
            </div>
          </div>
          <div class="donate-footer">
            <ul class="nav nav-tabs nav-justified" role="tablist">
              <li role="presentation" class="active">
                <a href="#alipay" id="alipay-tab" role="tab" data-toggle="tab" aria-controls="alipay" aria-expanded="true"><i class="icon icon-alipay"></i> 支付宝</a>
              </li>
              <li role="presentation" class="">
                <a href="#wechatpay" role="tab" id="wechatpay-tab" data-toggle="tab" aria-controls="wechatpay" aria-expanded="false"><i class="icon icon-wepay"></i> 微信支付</a>
              </li>
            </ul>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>



</main>

  <footer class="footer" itemscope itemtype="http://schema.org/WPFooter">
	
	
    <ul class="social-links">
    	
        <li><a href="/Skyung/null" target="_blank" title="Github" data-toggle=tooltip data-placement=top><i class="icon icon-github"></i></a></li>
        
        <li><a href="/Skyung/null" target="_blank" title="Weibo" data-toggle=tooltip data-placement=top><i class="icon icon-weibo"></i></a></li>
        
        <li><a href="/Skyung/null" target="_blank" title="Twitter" data-toggle=tooltip data-placement=top><i class="icon icon-twitter"></i></a></li>
        
        <li><a href="/Skyung/null" target="_blank" title="Behance" data-toggle=tooltip data-placement=top><i class="icon icon-behance"></i></a></li>
        
        <li><a href="/Skyung/atom.xml" target="_blank" title="Rss" data-toggle=tooltip data-placement=top><i class="icon icon-rss"></i></a></li>
        
    </ul>

    <div class="copyright">
    	
        <div class="publishby">
        	Theme by <a href="https://github.com/cofess" target="_blank"> cofess </a>base on <a href="https://github.com/cofess/hexo-theme-pure" target="_blank">pure</a>.
        </div>
    </div>
</footer>
  <script src="//cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js"></script>
<script>
window.jQuery || document.write('<script src="js/jquery.min.js"><\/script>')
</script>

<script src="/Skyung/js/plugin.min.js"></script>


<script src="/Skyung/js/application.js"></script>


    <script>
(function (window) {
    var INSIGHT_CONFIG = {
        TRANSLATION: {
            POSTS: '文章',
            PAGES: '页面',
            CATEGORIES: '分类',
            TAGS: '标签',
            UNTITLED: '(未命名)',
        },
        ROOT_URL: '/Skyung/',
        CONTENT_URL: '/Skyung/content.json',
    };
    window.INSIGHT_CONFIG = INSIGHT_CONFIG;
})(window);
</script>

<script src="/Skyung/js/insight.js"></script>






   




   
    
    <script defer type="text/javascript" src="http://v2.uyan.cc/code/uyan.js?uid=[object Object]"></script>








</body>
</html>